# Enc-db配置方法

## 1. 安装环境

> Ubuntu 20.04
> gcc version 9.4.0

### 1.1 依赖项

```shell
sudo apt-get update
sudo apt-get install -y build-essential libreadline-dev zlib1g-dev libssl-dev libxml2-dev libxslt-dev cmake
sudo apt-get install -y make automake libtool git
sudo apt-get install -y libssl-dev libboost-all-dev lzip
sudo apt-get install -y protobuf-compiler libprotobuf-dev
sudo apt-get install -y libjsoncpp-dev

# 安装gmp依赖
wget https://gmplib.org/download/gmp/gmp-6.2.1.tar.lz
tar -xf gmp-6.2.1.tar.lz
cd gmp-6.2.1
CFLAGS="-fPIC" ./configure
make -j4
sudo make install

# 安装log4cpp日志
wget http://sourceforge.net/projects/log4cpp/files/log4cpp-1.1.3.tar.gz
tar -xvzf log4cpp-1.1.3.tar.gz
cd log4cpp
./configure
make
sudo make install

# 安装国密依赖
git clone https://github.com/guanzhi/GmSSL.git
cd GmSSL
mkdir build
cd build
cmake ..
make
make test
sudo make install

# 安装DCAP组件
wget -qO- https://download.01.org/intel-sgx/sgx_repo/ubuntu/intel-sgx-deb.key \
  | sudo gpg --dearmor -o /usr/share/keyrings/intel-sgx.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/intel-sgx.gpg] \
  https://download.01.org/intel-sgx/sgx_repo/ubuntu focal main" \
  | sudo tee /etc/apt/sources.list.d/intel-sgx.list
sudo apt update
sudo apt install libsgx-dcap-ql libsgx-dcap-quote-verify \ 
libsgx-enclave-common libsgx-urts libsgx-dcap-default-qpl \
sgx-aesm-service

# 安装Open Enclave SDK依赖
wget -qO- https://packages.microsoft.com/keys/microsoft.asc \
  | sudo gpg --dearmor -o /usr/share/keyrings/microsoft-oe.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/microsoft-oe.gpg] \
  https://packages.microsoft.com/ubuntu/20.04/prod focal main" \
  | sudo tee /etc/apt/sources.list.d/openenclave.list
sudo apt update
sudo apt install open-enclave
vim ~/.bashrc
# vim 打开后，将以下内容插入
export PKG_CONFIG_PATH=${PKG_CONFIG_PATH}:/opt/openenclave/share/pkgconfig
export CMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}:/opt/openenclave/lib/openenclave/cmake
export PATH=${PATH}:/opt/openenclave/bin
export OE_SDK_PATH=/opt/openenclave
source ~/.bashrc
```

### 1.2 PostgreSQL安装

现版本为PostgreSQL-14.2

```shell
# 解压 PG
tar -xzvf postgresql_final.tar.gz

# 将pg编译到/usr/local/postgresql路径下
sudo mkdir /usr/local/postgresql
cd postgresql-14.2
./configure --enable-debug --enable-cassert --prefix=/usr/local/postgresql CFLAGS=-O0
sudo make -j `nproc`

# 编译encdb
cd src/interfaces/libpq/encryptsql/

rm -f ../Makefile
cp replaced_file/Makefile.libpq.14 ../Makefile
rm -f ../../../bin/psql/Makefile
cp replaced_file/Makefile.psql.14 ../../../bin/psql/Makefile
cp replaced_file/fe-exec.14.c ../
rm ../fe-exec.c
mv ../fe-exec.14.c ../fe-exec.c
sudo mkdir -p /etc/encryptsql/enclave
sudo cp config.json /etc/encryptsql/
ln -s ../../../include db_include

rm -rf build
mkdir build && cd build
cmake .. \
  -DENCRYPTSQL_INSTALL_DIR=/usr/local/postgresql \
  -DENCRYPTSQL_CONFIG_DIR=/etc/encryptsql \
  -DDK_SERVER_HOST=127.0.0.1 \
  -DDK_SERVER_PORT=9443
sudo make -j `nproc`

cd ../../../../../
cmake --build src/interfaces/libpq/encryptsql/build --target enclave.signed.so -j"$(nproc)"
cp /etc/encryptsql/enclave.signed.so src/interfaces/libpq/encryptsql/build/lib/enclave.signed.so
make install

sudo rm /usr/local/postgresql/lib/libudf.so
sudo cp src/interfaces/libpq/encryptsql/build/lib/libudf.so /usr/local/postgresql/lib

sudo rm /usr/local/postgresql/lib/libencryptsql.so
sudo cp src/interfaces/libpq/encryptsql/build/lib/libencryptsql.so /usr/local/postgresql/lib

sudo cp src/interfaces/libpq/encryptsql/createudf.sql /usr/local/postgresql/
sudo cp src/interfaces/libpq/encryptsql/map.json /etc/encryptsql
sudo cp src/interfaces/libpq/encryptsql/mask_funcs.sql /usr/local/postgresql/

sudo cp src/interfaces/libpq/encryptsql/build/bin/backup /usr/local/postgresql/bin/
sudo cp src/interfaces/libpq/encryptsql/build/bin/restore /usr/local/postgresql/bin/

# 创建cmk存储路径
sudo mkdir -p /etc/encryptsql/kms_test
sudo cp src/interfaces/libpq/encryptsql/src/KMS/domainkey /etc/encryptsql/kms_test/

cd /etc/encryptsql
sudo chmod 777 map.json

# 创建kms_root根密钥相关文件
sudo mkdir -p /etc/encryptsql/kms_root

cd /etc/encryptsql/kms_root
sudo head -c 16 /dev/urandom > frag_a
sudo head -c 16 /dev/urandom > frag_b
sudo head -c 16 /dev/urandom > frag_c
```

### 1.3 订阅 PCS 服务
登录 PCS 服务官网，在`Manage Subscription` 中查看 API 密钥
`https://api.portal.trustedservices.intel.com/products#product=liv-intel-software-guard-extensions-provisioning-certification-service` 
```shell
  //PCCS server address
  "pccs_url": "https://api.trustedservices.intel.com/sgx/certification/v4/"

  // To accept insecure HTTPS certificate, set this option to false
  ,"use_secure_cert": true

  // API key for accessing Intel Trusted Services
  ,"api_key": "得到的api_key"
```

### 1.4 配置 Enclave 签名密钥对
```shell
sudo mkdir -p /etc/encryptsql/enclave
# 生成 3072-bit RSA 私钥
sudo openssl genrsa -3 -out /etc/encryptsql/enclave/sign_enclave_private.pem 3072
# 从私钥导出公钥
sudo openssl rsa -in /etc/encryptsql/enclave/sign_enclave_private.pem -pubout \
  -out /etc/encryptsql/enclave/sign_enclave_public.pem
# 权限
sudo chmod 777 /etc/encryptsql/enclave/sign_enclave_private.pem
sudo chmod 777 /etc/encryptsql/enclave/sign_enclave_public.pem
```

### 1.5 配置数据库

```shell
cd /usr/local/postgresql
sudo mkdir data
sudo useradd -m -s /bin/bash postgres
sudo passwd postgres
sudo chown postgres:postgres data
su postgres

vim ~/.bashrc 
#vim打开后 将下面export开头内容插入到内部并保存
export PGHOME=/usr/local/postgresql
export PGDATA=$PGHOME/data 
export PGPORT=5432 
export PGUSER=postgres 
export PATH=$PGHOME/bin:$PATH:$HOME/bin 
export LD_LIBRARY_PATH=$PGHOME/lib 
source ~/.bashrc

initdb

# 配置 KeyDistribution 后台接收服务（由 postmaster 启动）
vim /usr/local/postgresql/data/postgresql.conf
# vim 打开后，将下面内容加入到尾部
shared_preload_libraries = 'keydist_receiver'
keydist.listen_host = '127.0.0.1'
keydist.listen_port = '9443'
keydist.enclave_signed_path = '/new_enc/postgresql-14.2/src/interfaces/libpq/encryptsql/build/bin/tls_server_enclave.signed'


pg_ctl start

su root
cd bin
./psql -U postgres
```
## 2. 使用说明

### 2.1 功能准备

```sql
create user dekmaster with password 'secure_password';

create database dekmaster owner dekmaster;

GRANT ALL PRIVILEGES ON DATABASE dekmaster TO dekmaster;

\q
```

```shell
./psql -U dekmaster -d dekmaster
```

配置DEK表

```sql
create table dek_store(
username text,
db text,
t text,
c text,
dek text);
\q
```
### 2.2 测试使用

```shell
./psql -U postgres -d postgres
```

进入psql客户端

#### 1. 密钥创建、建表插入查询功能

```sql
--导入密态UDF函数
\i ../createudf.sql

--启动加密环境
--(enc status可查询数据库enc加密状态，1为on开启中，0为off已关闭)
enc on;

--创建CMK,并设置轮转周期(天)
create cmk -rotate_period=30;

--创建表
CREATE TABLE sample_table (
  id int PRIMARY KEY,--数值明文
  datetime date,--日期明文
  name varchar(255) encrypted(eq),--字符密文，确定性加密
  brand char(20) encrypted(rnd) NOT NULL,--字符密文，非确定性加密
  amount int4 encrypted(eq, cal),--int4数值密文，确定性加密+同态加密
  price int8 encrypted(eq, cal),--int8数值密文，确定性加密+同态加密
  discount_price int8 encrypted(eq, cmp),--数值密文，确定性加密+保序加密
  supplier_name varchar(255),  --字符明文
  supplier_address text
);

--插入数据
INSERT INTO sample_table VALUES
(1, '2024-03-29', 'Product_1', 'BMFTOEKLWR', 10000, 2000, 1699, 'Supplier_1', '2 Road, District_9'),
(2, '2023-12-27', 'Product_2', 'AZZPSNNNHP', 20000, 1000, 890, 'Supplier_2', '7 Road, District_6'),
(3, '2020-03-13', 'Product_2', 'RJBVWGLNMK', 22500, 599, 469, 'Supplier_3', '1 Road, District_4'),
(4, '2020-04-21', 'Product_2', 'QLLCNGBDSV', 20000, 1200, 1099, 'Supplier_3', '1 Road, District_4'),
(5, '2020-01-10', 'Product_3', 'CPSLKAILDA', 5000, 2599, 2180, 'Supplier_2', '7 Road, District_6'),
(6, '2024-05-02', 'Product_3', 'WKGWZKTNSC', 15000, 1990, 1690, 'Supplier_4', '8 Road, District_2'),
(7, '2022-11-30', 'Product_4', 'PKDVPUEUEL', 6000, 5999, 5399, 'Supplier_3', '1 Road, District_4'),
(8, '2020-06-28', 'Product_5', 'ORXMPSQCQK', 2000, 4999, 4321, 'Supplier_1', '2 Road, District_9'),
(9, '2022-02-24', 'Product_6', 'CYKQRZFHTZ', 20, 18000, 16990, 'Supplier_5', '12 Road, District_3'),
(10, '2021-05-27', 'Product_7', 'NZJQYDWQCQ', 4000, 2700, 1688, 'Supplier_2', '7 Road, District_6');

SELECT * FROM sample_table;
```

---

#### 2. 常用密文SQL计算功能

```sql
--------------------------------常用SQL语句功能测试----------------------------------
--插入查询自动加解密(1.2.1)、明文功能正常使用(1.4.1)、密文标准SQL功能支持(1.4.4)
----------------逻辑运算(1.2.2)---------------
--等值运算(=, !=, GROUP BY)、比较运算(>, <, >=, <=, BETWEEN...AND, ORDER BY, 升序ASC, 降序DESC)、析取合取(AND, OR)等
--等值运算**仅支持在eq加密字段**密文上执行
--比较运算**仅支持在cmp加密字段**密文上执行
DELETE FROM sample_table WHERE id > 8;
UPDATE sample_table SET amount = 3000 WHERE datetime = '2020-06-28';
SELECT * FROM sample_table;
SELECT * FROM sample_table WHERE supplier_name != 'Supplier_3' OR  discount_price>=5000;
SELECT * FROM sample_table WHERE name = 'Product_2' AND discount_price < 1000;
SELECT * FROM sample_table WHERE discount_price BETWEEN 1000 AND 3000;
SELECT * FROM sample_table WHERE discount_price > 1500 ORDER BY discount_price DESC;
SELECT name, COUNT(*) AS nums FROM sample_table GROUP BY name;

----------------数值运算(1.2.3)--------------- 
--数学运算(+, -, *)、聚合函数(MAX, MIN, SUM, COUNT, POW)、混合类型运算(1.2.6)等
SELECT price,price+100 AS add_100 FROM sample_table WHERE name = 'Product_3';
SELECT amount,price,amount*price AS total_price FROM sample_table WHERE name = 'Product_4';
--int4, int8类型混合运算
SELECT amount,price,price + amount AS test_value FROM sample_table WHERE name = 'Product_4';
SELECT MAX(discount_price) AS max_value FROM sample_table;
SELECT MIN(discount_price) AS min_value FROM sample_table;
SELECT SUM(amount) AS sum_of_amount FROM sample_table WHERE id < 6;
SELECT COUNT(name) AS count_name FROM sample_table WHERE supplier_address != '2 Road, District_9';

```

---

#### 3. 密文索引功能

```sql
----------------------------------密文索引测试部分-----------------------------------
--密文索引能力比对(1.2.5): 验证支持构建高效密文索引
--在大量数据下才有所体现，这里仅作为功能展示

--关闭全表扫描
SET enable_seqscan TO off;
--建立索引前查询情况
explain analyze SELECT * FROM sample_table WHERE discount_price < 2000;
--在discount_price保序密文列上建立索引
CREATE INDEX idx_discount_price on sample_table(discount_price);
--建立索引后查询情况
explain analyze SELECT * FROM sample_table WHERE discount_price < 2000;

--在name确定性密文列上建立索引
explain analyze SELECT * FROM sample_table WHERE name = 'Product_2';
CREATE INDEX idx_name on sample_table(name);
explain analyze SELECT * FROM sample_table WHERE name = 'Product_2';

```

---

#### 4. 密钥轮转功能

```sql
---------------------------------密钥轮转测试部分------------------------------------
--密钥轮转(1.3.2): 现阶段支持CMK手动轮转和自动轮转，DEK随之同步更新；
--支持DEK的手动轮转，表级DEK、列级DEK或两者均支持；
--密钥用途唯一性(1.3.7): CMK仅用于加密DEK, DEK仅用于加密数据
--创建新表生成新DEK
CREATE TABLE t1(id int encrypted(eq));
--用DEK加密插入数据
INSERT INTO t1 VALUES (10),(20),(30);
SELECT * FROM t1;
--手动轮转CMK命令，同步更新DEK信息(大小写均可)
rotate cmk now;
--更新DEK后查询正常解密数据
SELECT * FROM t1;
DROP TABLE t1;

--自动轮转CMK命令，cmk rotate/rotation on/off/status;  开启/关闭/查询自动轮转(大小写均可)
--开启CMK自动轮转
cmk rotate on;
--查询CMK自动轮转状态
cmk rotate status;
--关闭CMK自动轮转
cmk rotate off;

--通过/new_enc/kms_test/CMK_auto_rotate_status.json文件持久化保存
--退出psql重新连接后，状态仍保持

--USAGE: ROTATE DEK NOW <table_name> [OPTIONS];
--DESCRIPTION: 轮换指定表的数据加密密钥(DEK)
--OPTIONS:
--TK           仅轮换表密钥
--CK <cols>    轮换指定列密钥，多个列名用逗号分隔
--ALL          轮换所有密钥（表密钥+所有列密钥）
--EXAMPLES:
ROTATE DEK NOW users -TK;
ROTATE DEK NOW users -CK `name`,`email`;
ROTATE DEK NOW users -ALL;
```

---

#### 5. 列级密钥相关功能

现完成列密钥的create、insert、update、select语句：

+ eq\eqsm4\rnd后面加 _c即可表示使用列密钥，其他加密种类暂不支持

```sql
enc on;

-- CREATE TABLE
CREATE TABLE sample ( 
  col1 decimal(10,2) encrypted(eq_c),
  col2 text encrypted(rnd_c),
  col3 text,
  col4 decimal(10,2) encrypted(eqsm4_c),
  col5 decimal(10,2) encrypted(eq,cmp,cal),
  col6 decimal(10,2) encrypted(eq,cmp,cal)
);

-- 插入语句
INSERT INTO sample (col1, col2, col3, col4, col5, col6) 
VALUES 
  (12.34, 'secret1', 'hello', 12.34, 90.12, 90.12),
  (12.34, 'secret2', 'world', 12.34, 12.34, 12.34),
  (12.34, 'secret3', 'nihao', 34.56, 34.56, 12.34),
  (-0.1234567890, 'secret4', 'world', 99.9999999999, 11.1111111111, 22.2222222222);
```

---

#### 6. 密钥查询功能

现完成密钥查询功能，提供查询CMK明密文的功能；
输出username、cmk_data、rotate_period、is_primary_version、is_rotated信息；
(DEK无感使用，不提供查询接口)：

```sql
enc on;

--查询CMK密文
describe cmk;

--查询CMK明文(解密后)
describe cmk -d;
-- 或
describe cmk -decrypt;
```

---

#### 7. 数据脱敏功能

现新增数据脱敏函数，函数集合在`mask_funcs.sql` 文件中；

+ 所有函数**统一返回**`text`**类型**，NULL值返回NULL值
+ 默认是缺省规则(保留前a位和后b位)
+ UDF函数**明密文状态下均可使用**
+ `insert`插入时脱敏(**静态脱敏**)，`select`查询时脱敏(**动态脱敏**)

- 也可`update`更新脱敏数据状态

具体函数如下：

```sql
SELECT mask('abcdefg', 2, 2)         AS default,  	--缺省规则(前a后b)
       mask_name('张三丰')           AS name_cn,  	  --中文名(后1)
       mask_name('John Doe')         AS name_en,  	--英文名(前1)
       mask_nick('小海浪子')         AS nick,		  --昵称(前1后1)
       mask_email('bob@example.com') AS email,		--邮箱(@后)
       mask_mobile('13812345678')    AS mobile,		--手机号(前3后2)
       mask_tel('010-88889999')      AS tel,		--固定电话(后2)
       mask_idcard('11010519900307563X') AS idcard, --身份证号(前1后1)
       mask_bankcard('6225888812345678') AS bank,	--银行卡号(前4后4)
       mask_plate('浙A12345')        AS plate,	    --车牌(前2后2)
       mask_ip('192.168.1.100')      AS ip,			--ip地址(前1字段)
       mask_numeric(123.456)         AS num,		--数值取整(四舍五入)
       mask_blank('anything')        AS blank,  	--置空(直接替换为-)
       mask_full('top secret')       AS full;		--全遮(保留长度，全替换为*)
       
       
--输出效果如下：
 name_cn | name_en  | nick |        email        |
---------+----------+------+---------------------+
 **丰    | J******* | 小*子 | *****@example.com  |

   mobile   |    tel     |      idcard       |
------------+------------+-------------------+
 138*****78 | ********99 | 1****************X |

        bank          |  plate   |     ip    |
----------------------+----------+-----------+
 6225************5678 | 浙A***45 | 192.*.*.* |

 num  | blank |   def   |   full
------+-------+---------+-----------
 123  | -     | ab***fg | **********

```

+ 可在密态数据库中通过输入语句：

    ```sql
    \i ../mask_funcs.sql
    ```

    执行文件编写UDF；

+ 也可以在进入psql界面之前，通过输入命令：

    ```bash
    psql -f mask_funcs.sql
    ```

    纳入版本库部署；

使用**静态脱敏**时，可在`insert` 时**直接调用函数**，例如：

```sql
CREATE TABLE customer (mobile text, email text);

INSERT INTO customer (mobile, email)
VALUES (mask_mobile('13812345678'),
        mask_email('alice@example.com'));
        
SELECT * FROM customer;
```

+ 若客户端 SQL 里不能显式写函数，可在**在 BEFORE INSERT 触发器里改写 NEW 值**统一处理，让客户端原样插入明文，由数据库自动脱敏后落盘，例：

    ```sql
    CREATE OR REPLACE FUNCTION trg_mask_customer()
    RETURNS trigger
    LANGUAGE plpgsql AS
    $$
    BEGIN
        NEW.mobile := mask_mobile(NEW.mobile);
        NEW.email  := mask_email(NEW.email);
        RETURN NEW;
    END;
    $$;
    
    CREATE TRIGGER bi_customer
    BEFORE INSERT ON customer
    FOR EACH ROW
    EXECUTE FUNCTION trg_mask_customer();
    ```

+ 客户端照旧 `INSERT INTO customer(mobile,email) VALUES('13812345678','alice@example.com');`，触发器会把列值替换成脱敏后的结果再写入.

使用**动态脱敏**时，通过生成列 / 视图，既保存原文又随时提供脱敏视图，

- 原数据写入真实列

- 建视图或在 SELECT 时调用脱敏函数，例：

    ```sql
    CREATE VIEW v_customer AS
    SELECT id,
           mask_name(name) AS name,
           mask_mobile(mobile) AS mobile,
           mask_email(email) AS email
    FROM customer;
    ```

# 备份命令
 su root 
 /usr/local/postgresql/bin/backup postgres
 
 # 导入命令
 su root 
 /usr/local/postgresql/bin/restore postgres
    
