GreatDB源码分析之表空间加密

概述

GreatDB(或mysql)服务端支持对表内数据进行加密存储,称之为表空间(table space)加密。使用方法和基本原理参考:

MySQL系列:表空间加密_mysql keyring_file_data-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/wngpenghao/article/details/123501766

GreatDB源代码:GreatSQL: GreatSQL is a MySQL branch originated from GreatDB (gitee.com)icon-default.png?t=N7T8https://gitee.com/GreatSQL/GreatSQL

基本原理

(本节内容摘自上述参考资料,并经过了排版和校对)

从5.7.11开始,mysql开始支持物理表空间的加密,它使用两层加密架构。包括:主密钥(master key) 和 表空间密钥(tablespace key)。

主密钥用于加密表空间密钥,加密后的表空间密钥存储在表空间文件的header中。表空间密钥用于加密数据。当用户想访问加密的表时,innoDB会先用 主密钥 对之前存储在表空间header中被加密的表空间密钥进行解密,得到明文的表空间密钥,再用表空间密钥解密数据信息。

表空间密钥是不会改变的,而主密钥可以通过命令随时改变。

代码结构

表空间加密功能由innodb和keyring插件共同实现。innodb负责使用表空间密钥加/解密表内数据,keyring负责管理主密钥。

Encryption类

innodb中表空间加密功能主要由Encryption类控制,其声明和实现在如下2个文件中:

storage/innobase/include/os0enc.h
storage/innobase/os/os0enc.cc

表空间密钥的生成

void Encryption::set_or_generate(Type type, byte *key, byte *iv,
                                 Encryption_metadata &metadata) {
  ut_ad(type != Encryption::NONE);
  metadata.m_type = type;
  metadata.m_key_len = Encryption::KEY_LEN;
  if (key == nullptr && iv == nullptr) {
    Encryption::random_value(metadata.m_key);
    Encryption::random_value(metadata.m_iv);
  } else if (key != nullptr && iv != nullptr) {
    memcpy(metadata.m_key, key, Encryption::KEY_LEN);
    memcpy(metadata.m_iv, iv, Encryption::KEY_LEN);
  } else {
    ut_error;
  }
}

表空间密钥先在Encryption::set_or_generate生成,存入一个Encryption_metadata结构,再通过Encryption::set_key设置。

加密

对表空间密钥进行加密:

/** Fill the encryption information.
  @param[in]      encryption_metadata  encryption metadata (key,iv)
  @param[in]      encrypt_key          encrypt with master key
  @param[out]     encrypt_info         encryption information
  @return true if success. */
  static bool fill_encryption_info(
      const Encryption_metadata &encryption_metadata, bool encrypt_key,
      byte *encrypt_info) noexcept;

对表内数据进行加密:

  /** Encrypt the redo log block.
  @param[in]      type      IORequest
  @param[in,out]  src_ptr   log block which need to encrypt
  @param[in,out]  dst_ptr   destination area
  @return true if success. */
  bool encrypt_log_block(const IORequest &type, byte *src_ptr,
                         byte *dst_ptr) noexcept;

  /** Encrypt the redo log data contents.
  @param[in]      type      IORequest
  @param[in,out]  src       page data which need to encrypt
  @param[in]      src_len   size of the source in bytes
  @param[in,out]  dst       destination area
  @param[in,out]  dst_len   size of the destination in bytes
  @return buffer data, dst_len will have the length of the data */
  byte *encrypt_log(const IORequest &type, byte *src, ulint src_len, byte *dst,
                    ulint *dst_len) noexcept;

  /** Encrypt the page data contents. Page type can't be
  FIL_PAGE_ENCRYPTED, FIL_PAGE_COMPRESSED_AND_ENCRYPTED,
  FIL_PAGE_ENCRYPTED_RTREE.
  @param[in]      type      IORequest
  @param[in,out]  src       page data which need to encrypt
  @param[in]      src_len   size of the source in bytes
  @param[in,out]  dst       destination area
  @param[in,out]  dst_len   size of the destination in bytes
  @return buffer data, dst_len will have the length of the data */
  [[nodiscard]] byte *encrypt(const IORequest &type, byte *src, ulint src_len,
                              byte *dst, ulint *dst_len) noexcept;

解密

解密表空间密钥:

/** Decoding the encryption info from the given array of bytes,
  which are assumed not to be related to any particular tablespace.
  @param[out]     encryption_metadata  decoded encryption metadata
  @param[in]      encryption_info      encryption info to decode
  @param[in]      decrypt_key          decrypt key using master key
  @return true if success */
  static bool decode_encryption_info(Encryption_metadata &encryption_metadata,
                                     const byte *encryption_info,
                                     bool decrypt_key) noexcept;

  /** Decoding the encryption info from the given array of bytes,
  which are assumed to be related to a given tablespace (unless
  space_id == dict_sys_t::s_invalid_space_id). The given tablespace
  is noted down in s_tablespaces_to_reencrypt if the encryption info
  became successfully decrypted using the master key and the space_id
  is not dict_sys_t::s_invalid_space_id. For such tablespaces the
  encryption info is later re-encrypted using the rotated master key
  in innobase_dict_recover().
  @param[in]      space_id        Tablespace id
  @param[in,out]  e_key           key, iv
  @param[in]      encryption_info encryption info to decode
  @param[in]      decrypt_key     decrypt key using master key
  @return true if success */
  static bool decode_encryption_info(space_id_t space_id, Encryption_key &e_key,
                                     const byte *encryption_info,
                                     bool decrypt_key) noexcept;

解密表内数据:

  /** Decrypt the log block.
  @param[in]      type  IORequest
  @param[in,out]  src   data read from disk, decrypted data
                        will be copied to this page
  @param[in,out]  dst   scratch area to use for decryption
  @return DB_SUCCESS or error code */
  dberr_t decrypt_log_block(const IORequest &type, byte *src,
                            byte *dst) noexcept;

  /** Decrypt the log data contents.
  @param[in]      type      IORequest
  @param[in,out]  src       data read from disk, decrypted data
                            will be copied to this page
  @param[in]      src_len   source data length
  @param[in,out]  dst       scratch area to use for decryption
  @return DB_SUCCESS or error code */
  dberr_t decrypt_log(const IORequest &type, byte *src, ulint src_len,
                      byte *dst) noexcept;

  /** Decrypt the page data contents. Page type must be
  FIL_PAGE_ENCRYPTED, FIL_PAGE_COMPRESSED_AND_ENCRYPTED,
  FIL_PAGE_ENCRYPTED_RTREE, if not then the source contents are
  left unchanged and DB_SUCCESS is returned.
  @param[in]      type    IORequest
  @param[in,out]  src     data read from disk, decrypt
                          data will be copied to this page
  @param[in]      src_len source data length
  @param[in,out]  dst     scratch area to use for decrypt
  @param[in]  dst_len     size of the scratch area in bytes
  @return DB_SUCCESS or error code */
  [[nodiscard]] dberr_t decrypt(const IORequest &type, byte *src, ulint src_len,
                                byte *dst, ulint dst_len) noexcept;

使用国密算法

GreatDB目前支持国密算法(SM4等),想要表空间加密使用国密算法,需要在cmake阶段加入“-DWITH_SSL_GM=ON”,代码无需改动。编译完成后,无法再通过配置文件或命令换回普通AES。

keyring

未完待续。。。。。。

  • 17
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值