Skip to content

Commit

Permalink
Merge pull request #11 from intersective/feature/vault
Browse files Browse the repository at this point in the history
Vault-based encryption for increased efficiency. All basic operations implemented. Closes #10 #5 #3
  • Loading branch information
uzyn committed Feb 26, 2017
2 parents f400b7d + e81df5b commit f8c2fcd
Show file tree
Hide file tree
Showing 20 changed files with 1,596 additions and 417 deletions.
49 changes: 32 additions & 17 deletions Config/Schema/schema.php
Expand Up @@ -8,32 +8,47 @@ public function before($event = array()) {
public function after($event = array()) {
}

public $lapis_keys = array(
'id' => array('type' => 'integer', 'null' => false, 'default' => null, 'length' => 11, 'key' => 'primary'),
'created' => array('type' => 'datetime', 'null' => true, 'default' => null),
'modified' => array('type' => 'datetime', 'null' => true, 'default' => null),
'parent_id' => array('type' => 'integer', 'null' => true, 'default' => null),
'parent_id' => array('type' => 'integer', 'null' => true, 'default' => null),
'public_key' => array('type' => 'text', 'null' => false),
'private_key' => array('type' => 'text', 'null' => true, 'default' => null),
'active' => array('type' => 'boolean', 'null' => false, 'default' => true),
public $lapis_requesters = array(
'id' => array('type' => 'string', 'null' => false, 'default' => null, 'length' => 36, 'key' => 'primary'),
'created' => array('type' => 'datetime'),
'modified' => array('type' => 'datetime'),
'parent_id' => array('type' => 'string', 'length' => 36, 'null' => true, 'default' => null),
'ident_public_key' => array('type' => 'text', 'null' => false),
'ident_private_key' => array('type' => 'text', 'null' => true, 'default' => null),
'vault_public_key' => array('type' => 'text', 'null' => true, 'default' => null),
'vault_private_key' => array('type' => 'text', 'null' => true, 'default' => null),
'indexes' => array(
'PRIMARY' => array('unique' => true, 'column' => 'id')
),
'tableParameters' => array()
);

public $lapis_accessors = array(
'id' => array('type' => 'string', 'null' => false, 'default' => null, 'length' => 36, 'key' => 'primary'),
'created' => array('type' => 'datetime'),
'modified' => array('type' => 'datetime'),
'vault_id' => array('type' => 'string', 'length' => 36, 'null' => true, 'default' => null),
'requester_id' => array('type' => 'string', 'length' => 36, 'null' => true, 'default' => null),
'key' => array('type' => 'text', 'null' => true, 'default' => null),
'indexes' => array(
'PRIMARY' => array('unique' => true, 'column' => 'id'),
'lapis_vaults_vault_id' => array('column' => 'vault_id'),
'lapis_vaults_requester_id' => array('column' => 'requester_id')
),
'tableParameters' => array()
);

public $lapis_documents = array(
'id' => array('type' => 'binary', 'null' => false, 'default' => null, 'length' => 256, 'key' => 'primary'),
'created' => array('type' => 'datetime', 'null' => true, 'default' => null),
'modified' => array('type' => 'datetime', 'null' => true, 'default' => null),
'key_id' => array('type' => 'integer', 'null' => false, 'default' => null, 'length' => 11),
'model_id' => array('type' => 'binary', 'null' => false, 'default' => null, 'length' => 256),
'document_pw' => array('type' => 'text', 'null' => false),
'id' => array('type' => 'string', 'null' => false, 'default' => null, 'length' => 36, 'key' => 'primary'),
'created' => array('type' => 'datetime'),
'modified' => array('type' => 'datetime'),
'model_id' => array('type' => 'string', 'null' => false, 'length' => 40),
'vault_id' => array('type' => 'string', 'length' => 36, 'null' => true, 'default' => null),
'key' => array('type' => 'text', 'null' => false),
'indexes' => array(
'PRIMARY' => array('unique' => true, 'column' => 'id'),
'lapis_key_id' => array('column' => 'key_id'),
'lapis_model_id' => array('column' => 'model_id'),
'lapis_documents_model_id' => array('column' => 'model_id'),
'lapis_documents_vault_id' => array('column' => 'vault_id')
),
'tableParameters' => array()
);
Expand Down
73 changes: 58 additions & 15 deletions Lib/Lapis.php
Expand Up @@ -67,12 +67,10 @@ public static function pwDecrypt($data, $password, $cipher = 'aes-256-ctr') {
* @return array Array with the following elements:
* 'cipher' => $cipher used, in plaintext
* 'data' => Symmetrically encrypted string
* 'key' => Public key-encrypted key for symmetric encryption
* 'iv' => Public key-encrypted iv for symmetric encryption
*/
public static function docEncrypt($document, $publicKeys, $options = array()) {
public static function docEncryptForMany($document, $publicKeys, $options = array()) {
if (is_string($publicKeys)) {
return static::docEncryptForOne($document, $publicKeys, $options);
$publicKeys = array($publicKeys);
}

$options = array_merge(array(
Expand All @@ -84,18 +82,24 @@ public static function docEncrypt($document, $publicKeys, $options = array()) {
$document = json_encode($document);
}

$ivLength = openssl_cipher_iv_length($options['cipher']);
$key = openssl_random_pseudo_bytes($options['keyLength']);
$iv = openssl_random_pseudo_bytes($ivLength);
if (empty($options['key']) || empty($options['iv'])) {
$ivLength = openssl_cipher_iv_length($options['cipher']);
$key = openssl_random_pseudo_bytes($options['keyLength']);
$iv = openssl_random_pseudo_bytes($ivLength);
} else {
$key = $options['key'];
$iv = $options['iv'];
}
$ciphertext = openssl_encrypt($document, $options['cipher'], $key, OPENSSL_RAW_DATA, $iv);
$data = $iv . $ciphertext;

$keys = array();
foreach ($publicKeys as $i => $publicKey) {
if (!openssl_public_encrypt($key, $encKey, $publicKey)) {
$keys[$i] = static::simplePublicEncrypt($key, $publicKey);

if ($keys[$i] === false) {
return false;
}
$keys[$i] = base64_encode($encKey);
}

return array(
Expand All @@ -106,16 +110,16 @@ public static function docEncrypt($document, $publicKeys, $options = array()) {
);
}

public static function docEncryptForOne($document, $publicKey, $options = array()) {
$results = static::docEncrypt($document, array($publicKey), $options);
public static function docEncrypt($document, $publicKey, $options = array()) {
$results = static::docEncryptForMany($document, array($publicKey), $options);
if (isset($results['keys'][0])) {
$results['key'] = $results['keys'][0];
unset($results['keys']);
}
return $results;
}

public static function docDecrypt($docData, $encDocKey, $privateKey) {
public static function docDecrypt($docData, $encDocKey, $privateKey, &$secret = null) {
if (is_string($docData)) {
$docData = json_decode($docData);
}
Expand All @@ -124,18 +128,57 @@ public static function docDecrypt($docData, $encDocKey, $privateKey) {
$ivLength = openssl_cipher_iv_length($cipher);
$data = base64_decode($docData->data);

if (!openssl_private_decrypt($encDocKeyDecoded, $key, $privateKey)) {
return false;
}
$key = static::simplePrivateDecrypt($encDocKeyDecoded, $privateKey);
if ($key === false) {
return false;
}

$iv = substr($data, 0, $ivLength);
$ciphertext = substr($data, $ivLength);

$document = openssl_decrypt($ciphertext, $cipher, $key, OPENSSL_RAW_DATA, $iv);

// For updating of document without generating new keys
$secret = array(
'iv' => $iv,
'key' => $key
);

$docArray = json_decode($document, true);
if (!$docArray) {
return $document;
}
return $docArray;
}

/**
* Simple public key encryption
* Note: this may fail if data is longer than what's supposed by the public key length, use docEncrypt() for most the safest public key encryption. This method is meant more for internal key handling use
* @param string $data Data to be encrypted
* @param mixed $publicKey Public key
* @return mixed Base64-encoded encryption result or false on failure.
*/
public static function simplePublicEncrypt($data, $publicKey) {
if (!openssl_public_encrypt($data, $crypted, $publicKey)) {
return false;
}
return base64_encode($crypted);
}

/**
* Simple private key decryption
* @param string $data Data to be decrypted
* @param mixed $privateKey Plain text private key
* @return mixed Decryption result, or false on failure
*/
public static function simplePrivateDecrypt($data, $privateKey) {
try {
if (!openssl_private_decrypt($data, $result, $privateKey)) {
return false;
}
} catch (Exception $e) {
return false;
}
return $result;
}
}
14 changes: 14 additions & 0 deletions Model/Accessor.php
@@ -0,0 +1,14 @@
<?php
App::uses('AppModel', 'Model');
App::uses('Lapis', 'Lapis.Lib');

/**
* Vault accessors
*
* `key` field in this table is to be combined with the `vault_private_key` field from Lapis.Requester
*/
class Accessor extends AppModel {
public $tablePrefix = 'lapis_';
public $name = 'Accessor';
}

0 comments on commit f8c2fcd

Please sign in to comment.