|
Andy Niu Help
1.0.0.0
|
变量 | |
| 和lua的交互流程 | |
| 类的层次化结构 | |
| 洋葱层调整测试 | |
| 加解密元数据的读写 | |
| 加解密元数据的读操作 | |
| 加解密元数据的写操作 | |
详细描述
变量说明
| 加解密元数据的写操作 |
1、CreateDelta::apply
deltaOutputBeforeQuery insert到 generic_prefix_BleedingMetaObject
deltaOutputAfterQuery insert到 generic_prefix_MetaObject
2、这些可以怎么来的,在什么地方插入到数据库?
序列化写入到数据库中
--> " INSERT INTO " + table_name + " (serial_object, serial_key, parent_id, id) VALUES (
--> esc_child_serial = escapeString(e_conn, child_serial); // mysql转义
--> object.serialize(parent) // 序列化,看一下DBMeta,找到各个序列化的实现
--> EncLayer::serialize
--> return serial_pack(this->level(), this->name(), this->doSerialize());
--> doSerialize() 查看EncLayer 子类的doSerialize多态做法。
找两个例子
DETJOIN_int
--> DET_abstract_integer::doSerialize()
--> getCInteger_().serialize();
--> CryptedInteger::serialize() 对以下字段序列化
serializeStrings({key,TypeText<enum enum_field_types>::toText(field_type),std::to_string(inclusiveRange.first),std::to_string(inclusiveRange.second)});
--> DETJOIN_int新增加了字段cinteger,重写getCInteger_()方法,在DETJOIN_int(const Create_field &cf, const std::string &seed_key)创建
注:DETJOIN_int(unsigned int id, const CryptedInteger &cinteger)读取时候用到的
DETJOIN_str
--> std::string doSerialize() const {return rawkey;}
--> rawkey(prng_expand(seed_key, key_bytes))
--> 都是找到seed_key
--> DETJOINFactory::create(const Create_field &cf,const std::string &key)
--> EncLayerFactory::encLayer(onion o, SECLEVEL sl, const Create_field &cf,const std::string &key)
--> const std::string key = m_key ? getLayerKey(m_key, uniqueFieldName, l) : "plainkey";
--> OnionMeta::OnionMeta(onion o, std::vector<SECLEVEL> levels,const AES_KEY * const m_key,const Create_field &cf, unsigned long uniq_count,SECLEVEL minimum_seclevel)
--> static bool init_onions_layout(const AES_KEY *const m_key, FieldMeta *const fm,const Create_field &cf, bool unique)
--> FieldMeta::FieldMeta(const Create_field &field,const AES_KEY * const m_key,SECURITY_RATING sec_rating,unsigned long uniq_count,bool unique)
--> a.getMasterKey().get()
--> createAndRewriteField(Analysis &a,xxx)
--> class CreateTableHandler::rewriteAndUpdate(Analysis &a, LEX *lex, const Preamble &pre) const
--> AbstractQueryExecutor *DDLHandler::transformLex(Analysis &a, LEX *lex) const
--> Rewriter::dispatchOnLex(Analysis &a, const std::string &query)
--> Analysis analysis(default_db, schema, ps.getMasterKey(),ps.defaultSecurityRating());
--> ps.getMasterKey()
--> Rewriter::rewrite(const std::string &q, const SchemaInfo &schema,const std::string &default_db, const ProxyState &ps)
--> return shared.getMasterKey();
--> const SharedProxyState &shared;
--> ProxyState *const ps = thread_ps = c_wrapper->ps.get();
--> WrapperState *const c_wrapper = clients[client];
clients[client]-->WrapperState-->ProxyState-->SharedProxyState
--> clients[client]->ps = std::unique_ptr<ProxyState>(new ProxyState(*shared_ps));
--> SharedProxyState::SharedProxyState(ConnectionInfo ci,const std::string &embed_dir,const std::string &master_key,SECURITY_RATING default_sec_rating)
: masterKey(std::unique_ptr<AES_KEY>(getKey(master_key))),
embed_dir(embed_dir),
mysql_dummy(SharedProxyState::db_init(embed_dir)),
--> getKey(master_key)获得 AES_KEY
--> const std::string &mkey = "113341234";
shared_ps = new SharedProxyState(ci, embed_dir, mkey, determineSecurityRating()); //shared_ps 包含conn e_conn
| 加解密元数据的读写 |
1、一个字段会扩展为多个字段,用于不同的同态加密。扩展后的一个字段,也就是一个洋葱,一个洋葱对应多层加密,每层加密就是一个洋葱层。
2、每个洋葱层,需要确定加密算法和秘钥。
3、加密算法是由所支持的同态运算所决定的,比如 支持int的同态加密,使用DET_int。
4、秘钥是怎么来的?
秘钥是创建表的时候生成的,转化的步骤很多。参数的来源在哪里?
5、秘钥需要持久化,保存在哪里?
在embedded_db,递归调用读取。
| 加解密元数据的读操作 |
1、类图
SharedProxyState<>——SchemaCache——>SchemaInfo<>——DatabaseMeta<>——TableMeta<>——FieldMeta<>——OnionMeta<>——EncLayer
SchemaInfo对应一个数据库的加密元数据
2、递归调用,load加密元数据
std::function<DBMeta *(DBMeta *const)> loadChildren =
[&loadChildren, &e_conn](DBMeta *const parent) {
auto kids = parent->fetchChildren(e_conn);
for (auto it : kids) {
loadChildren(it);
}
return parent; /* lambda */
};
loadChildren(schema.get());
堆栈可以看到,__lambda5::operator() rewrite_main.cc:607
构建秘钥,从数据库读取,然后反序列化。
3、数据的crud操作(增删改查)涉及到加解密,那就需要用到秘钥,每次从embedded_db读取,效率太低。
需要把数据load到内存中,这就是涉及到同步,保持一致性。怎么解决这个问题?
a、SchemaCache有个字段no_loads表示是否加载元数据,初始化为 true,表示没有加载,启动proxy,第一次tlogin,no_loads设置为false
b、同时【INSERT INTO embedded_db.generic_prefix_staleness (cache_id, stale) VALUES (2313589110, TRUE);】 stale表示过时的数据,表示需要更新。
c、每次判断是否需要重新读取,都是 【SELECT stale FROM embedded_db.generic_prefix_staleness WHERE cache_id = 3209647041;】检查这个取值。
d、在哪里更新stale?
SchemaCache::updateStaleness,为了保证已经存在的记录,也需要重读,把之前的设置为true
4、哪些情况判断是否读取?直接看调用图
SchemaCache::getSchema
rewrite 每次rewrite都要判断
OnionAdjustmentExecutor::nextImpl(const ResType &res, const NextParams &nparams) 洋葱层调整,需要再次判断
ShowTablesExecutor::nextImpl(const ResType &res, const NextParams &nparams)
SpecialUpdateExecutor::nextImpl(const ResType &res, const NextParams &nparams)
5、哪些情况更新stale值
SchemaCache::updateStaleness
AbstractQueryExecutor::next --> AbstractQueryExecutor::genericPreamble --> nparams.ps.getSchemaCache().updateStaleness(nparams.ps.getEConn(), this->stales());
| 和lua的交互流程 |
1、read_query
read_query --> read_query_real --> next_handler --> CryptDB.next
接收客户端的请求sql,进行转化,return proxy.PROXY_SEND_QUERY
改写后的sql语句,通过 proxy.queries:append 传递过去。
2、read_query_result
read_query_result --> read_query_result_real --> next_handler --> CryptDB.next
接收mysql的返回,进行处理,return proxy.PROXY_SEND_RESULT
处理后,如果有结果集需要返回,通过 proxy.response.resultset 传递过去。
3、proxy与mysql交互,从mysql返回之后,必定进入 read_query_result
分为几种情况:
a、不需要处理,比如 show databases; 直接返回
b、只需要处理一次,比如 show tables; 解密一些表名即可
c、需要处理多次,比如create table;
4、分析show databases;的流程:
a、客户端发给proxy请求show databases,read_query接口返回PROXY_SEND_QUERY,同时传递改写后的sql语句,还是show databases
b、内部的next接口返回query-results,把skip置为true
b、proxy和mysql交互,从mysql收到响应包,在read_query_result_real检查到是skip,直接返回。
5、分析show tables;的流程:
a、客户端发给proxy请求show tables,read_query接口返回PROXY_SEND_QUERY,同时传递改写后的sql语句,还是show tables
b、内部的next接口返回again
c、proxy和mysql交互,从mysql收到响应包,在read_query_result_real,打印响应的结果,然后 next_handler("results",
d、next返回"results",解密返回,return proxy.PROXY_SEND_RESULT,也就是说,先检查next的返回,再检查从哪里来的?
6、分析create table;的流程:
a、客户端发给proxy请求create table,read_query接口返回PROXY_SEND_QUERY,同时传递改写后的sql语句,create tablexxx
b、内部的next接口返回again
c、proxy和mysql交互,从mysql收到响应包,在read_query_result_real,打印响应的结果,然后 next_handler("results",
d、next返回"again",告诉proxy忽略mysql的返回。
e、proxy和mysql交互,从mysql收到响应包,next返回"results"
7、也就是说,
第一次的改写返回PROXY_SEND_QUERY,
中间过程的改写,返回PROXY_IGNORE_RESULT
最后一步的处理,返回PROXY_SEND_RESULT
8、CryptDB.next的返回有三个值:
again 要求proxy和mysql交互,并且再次进来,处理mysql的响应
query-results mysql的响应,到lua之后,lua直接返回
results 返回最后处理的结果,要求proxy转发给客户端
9、proxy收到的回复有三个值:
PROXY_SEND_QUERY 向mysql发送请求
PROXY_SEND_RESULT 向客户端发送最后的结果
PROXY_IGNORE_RESULT 忽略中间过程的结果
10、总结一下,也就是说:
第一步是改写,要求proxy和mysql交互,
中间步骤处理返回的结果,比如剥洋葱,或者插入到remote_db,再次要求proxy和mysql交互,并且proxy忽略mysql的返回
最后一步,改写mysql返回的结果集。
| 洋葱层调整测试 |
1、剥洋葱,已经插入的数据,通过update更新掉,新插入的数据,加密层也会发生变化,少一层加密
2、准备测试数据如下:
start transaction;
create table student(id int, name varchar(64));
insert into student(id ,name) value (1,"Andy");
insert into student(id ,name) value (2,"Bill");
insert into student(id ,name) value (3,"Caroline");
insert into student(id ,name) value (4,"David");
commit;
3、测试洋葱层调整
select * from student where id =2;
4、按道理,洋葱层会少一层,在哪里处理的?
之前查回来的结果,和现在查回来的结果,对照一下。
5、在哪里删除RND记录的?
deltaOutputBeforeQuery-->bool DeleteDelta::apply
" DELETE embedded_db.generic_prefix_BleedingMetaObject FROM embedded_db.generic_prefix_BleedingMetaObject WHERE embedded_db.generic_prefix_BleedingMetaObject.id = 252 AND embedded_db.generic_prefix_BleedingMetaObject.parent_id = 249;"
deltaOutputAfterQuery-->DeleteDelta::apply
" DELETE embedded_db.generic_prefix_MetaObject FROM embedded_db.generic_prefix_MetaObject WHERE embedded_db.generic_prefix_MetaObject.id = 252 AND embedded_db.generic_prefix_MetaObject.parent_id = 249;"
| 类的层次化结构 |
1、DBMeta是数据库的元数据类,子类MappedDBMeta是类模板,字段包括一个map结构,保存child数据结构。
2、MappedDBMeta会实例化一些模板类,比如MappedDBMeta<DatabaseMeta,IdentityMetaKey>,就是包含一组DatabaseMeta的集合
3、包含一组DatabaseMeta的集合,其实也就是 SchemaInfo,SchemaInfo继承 MappedDBMeta<DatabaseMeta,IdentityMetaKey>
4、DatabaseMeta、TableMeta、FieldMeta也是同样的道理。
5、TableMeta关注以下几个字段,分别为:
counter 因为一个表的字段,是有顺序的,counter记录当前的counter值
anon_table_name 是加密后的表名
注意:IdentityMetaKey 是由明文的表名,序列化出来的
tm->addChild(IdentityMetaKey(name), std::move(fm));
明文的表名-->IdentityMetaKey-->TableMeta-->anno_table_name
6、FieldMeta关注以下几个字段,分别为:
fname 字段名称
salt_name 前缀加上随机值
onion_layout 是字段扩展后的洋葱,表示一个字段对应一组洋葱(map),以及每个洋葱对应的一组洋葱层(vector)。
7、OnionMeta不同于上面的结构,不是继承MappedDBMeta,而是直接继承DBMeta,因为OnionMeta直接关联vector的EncLayer,而不是map结构
onionname是洋葱字段名称
8、EncLayer就是叶子结点,继承了LeafDBMeta,在LeafDBMeta中对叶子节点 do nothing
EncLayer代表了一个加密层次的抽象
Copyright (c) 2015~2016, Andy Niu @All rights reserved. By Andy Niu Edit.