GAN增強網絡安全

密碼技術與我們息息相關,使用密碼技術不僅僅能夠保證信息的機密性,而且可以保證信息的完整性和可用性,防止信息被篡改、僞造和假冒。一直以來,設計和破解密碼都是人類的專利,然而,隨着人工智能的發展,Google Brain的研究成果《讓神經對抗網絡學習保護通信》(learning to Protect Communications with Adversarial Neural Cryptography), 試圖讓用0和1思考的機器學習對信息進行加密。

下面簡要介紹一下對稱加密算法:假設兩個人要進行通信,定義爲Alice和Bob,很自然,Alice和Bob想要私密的通信,可是有一個破壞者Eve想要竊取Alice和Bob之間的通信信息,所以Alice就把要發送的信息(我們稱其爲明文P)用祕鑰K進行加密得到密文C,然後將密文C發送給Bob,Eve和Bob都能夠接觸到密文C,由於Bob手中有祕鑰K,就可以將密文C恢復成明文P,也就是能夠得到Alice發送的信息,然而Eve僅僅能夠接觸到密文,沒有祕鑰,所以就不能夠得到明文P, 這就是對稱加密算法。

對稱加密系統

Alice,Bob,Eve三個都是神經網絡,它們的參數分別爲, 。我們向神經網絡Alice中投入明文P和祕鑰K數據,它的計算結果會作爲密文C,與祕鑰K一起交給另一個神經網絡Bob,並優Bob進行解密得到。Eve接收到密文C, 試圖去恢復明文P,得到。這三個神經網絡模塊共同構成了對抗神經網絡的主體,Alice和Bob組成的網絡看做GAN中的生成模型G, 而Eve則被看做GAN中的批判模型D,我們希望Eve恢復祕鑰的概率爲0.5,在這裏是對等於恢復祕鑰的位數爲明文的1/2,即相當於瞎猜一樣。

下面介紹一些網絡的細節:

  • Alice和Bob有着相同的網絡結構,但是輸入,輸出和參數不同。
  • P和K是由-1和1組成的比特串,是位於區間(-1,1)之間的浮點數,在最後計算準確率的時候,把中的浮點數轉換成整數-1和1,大於等於0的爲1,小於0的則爲-1。
  • 每一個明文對應一個密文
Alice、Bob和Eve神經網絡的輸入和輸出
網絡模型 輸入 輸出
Alice P, K
Bob C, K
Eve C

損失函數

Eve目標是能夠準確地從竊聽到的密文C中恢復出明文P,也就是說要儘可能地縮小P和之間誤差,選擇使用L1範數作爲Eve的損失函數,其中d就是指的是P和之間的L1範數值。

Alice和Bob的目標不僅僅使得能夠 使得P和之間的誤差比較小,而且還要能夠安全準確地進行通信,Alice和Bob之間的損失函數爲:

網絡架構

首先一個全連接層,輸入數量等於輸出數量,全連接層的輸入爲明文P和祕鑰K,全連接層後面跟着4個1-D的卷積層,每個卷積層的window size,input depth輸入深度和output depth輸出深度分別爲[4, 1, 2], [2, 2, 4], [1, 4, 4]和[1, 4, 1],步長strides分別爲1,2,1,1,這樣,最後一層的向量的個數爲明文長度。除了最後一個卷積層,其他層(全連接層+3個卷積層)的激活函數爲sigmoid函數,最後一個卷積層的激活函數爲tanh函數,是因爲要使得的取值位於(-1,1)區間。下圖是Alice神經網絡的架構,由於Bob和Alice網絡架構相似,不同的是Bob的輸入爲密文C和祕鑰K。

由於Eve的輸入僅僅只有祕鑰K,所以網絡架構和Alice和Bob的略微有些差別。需要在全連接層前面再多加上一個N*2N全連接層

結果

下面的訓練結果取自明文和密文的長度都是16, minibatch的大小爲4096。理想的結果應當時Bob恢復明文的錯誤比特數爲0,Eve恢復明文的比特數爲8。在這個例子中,訓練剛開始,Bob和Eve的錯誤位數都是8, 對於一個16位的二進制文本來說,隨便生成的16位數字在概率上錯誤位數就是8--這時候Bob和Eve都還沒有具備解密的能力,伴隨的神經網絡之間的對抗,每個神經網絡不斷的調整參數值,Alice和Bob也逐漸獲得了極強的加密和解密能力。 當迭代7000次的時候,Bob預測明文的錯誤率大幅度下滑,在15000次迭代之後,Bob恢復明文的錯誤率已經幾乎爲0,而這時Eve的錯誤位數仍然在8左右波動,也就是說Eve的破解密碼的能力基本上等同於瞎猜,所以這時候Alice發出的信息非常安全 。

代碼實現

import tensorflow as tf
import numpy as np
import Net
import os

plainTextLength = 16
keyLength = 16
N = plainTextLength / 2
batch = 4096
learningRate = 0.0008
TRAIN_STEP= 20000
iterations = 1



def get_random_block(N, batch):
    return 2 * np.random.randint(2, size=(batch, N)) - 1


def train():
    with tf.name_scope('input_variable'):
        plain = tf.placeholder(tf.float32, shape=[None, plainTextLength], name='plainText')
        key = tf.placeholder(tf.float32, shape=[None, keyLength], name='keyText')

    Zeros = tf.zeros_like(plain, dtype=tf.float32, name='zeroVector')

    #
    Alice_output, Bob_output, Eve_output = Net._build_Network(plain, key, plainTextLength, keyLength)
    reshape_Bob_output = tf.reshape(Bob_output, shape=[-1, plainTextLength])
    reshape_Eve_output = tf.reshape(Eve_output, shape=[-1, plainTextLength])
    # Bob L1 loss
    with tf.name_scope('Bob_loss'):
        Bob_loss = tf.reduce_mean(tf.abs(reshape_Bob_output - plain))
    tf.summary.scalar('Bob_loss_value', Bob_loss)
    # Eve L1 Loss
    with tf.name_scope('Eve_loss'):
        Eve_loss = tf.reduce_mean(tf.abs(reshape_Eve_output - plain))
    tf.summary.scalar('Eve_loss_value', Eve_loss)
    # Alice_Bob Loss
    with tf.name_scope('A_B_loss'):
        Alice_Bob_loss = Bob_loss + (1 - Eve_loss) ** 2
    tf.summary.scalar('Alice_Bob_loss_value', Alice_Bob_loss)

    # error
    boolean_P = tf.greater(plain, Zeros)
    boolean_B = tf.greater_equal(reshape_Bob_output, Zeros)
    boolean_E = tf.greater_equal(reshape_Eve_output, Zeros)
    accuracy_B = tf.reduce_mean(tf.cast(tf.equal(boolean_B, boolean_P), dtype=tf.float32))
    accuracy_E = tf.reduce_mean(tf.cast(tf.equal(boolean_E, boolean_P), dtype=tf.float32))
    Bob_bits_wrong = plainTextLength - accuracy_B * plainTextLength
    Eve_bits_wrong = plainTextLength - accuracy_E * plainTextLength
    tf.summary.scalar('accuracy_B_value', accuracy_B)
    tf.summary.scalar('accuracy_E_value', accuracy_E)
    tf.summary.scalar('Bob_bits_wrong', Bob_bits_wrong)
    tf.summary.scalar('Eve_bits_wrong', Eve_bits_wrong)

    A_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, 'Alice')
    B_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, 'Bob')
    AB_vars = A_vars + B_vars
    Eve_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, 'Eve')

    Alice_Bob_optimizer = tf.train.AdamOptimizer(learningRate).minimize(Alice_Bob_loss, var_list=AB_vars)
    Eve_optimizer = tf.train.AdamOptimizer(learningRate).minimize(Eve_loss, var_list=Eve_vars)

    merged = tf.summary.merge_all()

    with tf.Session() as session:
        session.run(tf.global_variables_initializer())
        train_writer = tf.summary.FileWriter('F:\\MachineLearning+DeepLearningCode\\adversarial_crypto\\adver_logs', session.graph)
        if not os.path.exists('adver_logs'):
            os.makedirs('adver_logs')
        for step in range(TRAIN_STEP):
            # train Bob
            feedDict = {plain: get_random_block(plainTextLength, batch),
                        key: get_random_block(keyLength, batch)}
            for index in range(iterations):
                _, Bob_error, Bob_accuracy, Bob_wrong_bits = session.run(
                    [Alice_Bob_optimizer, Bob_loss, accuracy_B, Bob_bits_wrong], feed_dict=feedDict)
            Bob_accuracy_bits = Bob_accuracy * plainTextLength
            # train Eve
            Eve_feedDict = {plain: get_random_block(plainTextLength, 2 * batch),
                            key: get_random_block(keyLength, 2 * batch)}
            for index in range(2 * iterations):
                session.run(Eve_optimizer, feed_dict=Eve_feedDict)
            Eve_error, Eve_accuracy, Eve_wrong_bits = session.run([Eve_loss, accuracy_E, Eve_bits_wrong], feed_dict=Eve_feedDict)
            Eve_accuracy_bits = Eve_accuracy * plainTextLength
            AB_error, summary = session.run([Alice_Bob_loss, merged], feed_dict=feedDict)
            '''
            if step % 500 == 0:
                print('Step:', step)
                print('Eve_error:', Eve_error, 'Eve accuracy bits', Eve_accuracy_bits, '  AB_error:', AB_error)
                print('Bob_loss:', Bob_error, '  Bob Accuracy Bits:', Bob_accuracy_bits)
            '''
            train_writer.add_summary(summary, step)

def main(argv=None):
    train()

if __name__ == '__main__':
    tf.app.run()
#Net.py
import tensorflow as tf

def _conv1D(input, filter, stride, kernelSize, name, activation = tf.nn.sigmoid):
    with tf.variable_scope(name):
        return tf.layers.conv1d(inputs=input, filters=filter, strides=stride,
                                   kernel_size=kernelSize, padding='SAME', activation=activation, use_bias=False)

def _ConvNet(input, unitsLength):
    """
    構建網絡架構
    """
    input = tf.convert_to_tensor(input, dtype=tf.float32)
    input = tf.reshape(input, shape=[-1, unitsLength, 1])
    # print(input.shape)
    with tf.name_scope('convlayers'):
        conv1 = _conv1D(input, 2, 1, [4], name='conv_1')
        conv2 = _conv1D(conv1, 4, 2, [2], name='conv_2')
        conv3 = _conv1D(conv2, 4, 1, [1], name='conv_3')
        output = _conv1D(conv3, 1, 1, [1], name='conv_4', activation=tf.nn.tanh)
        return output

def _build_Network(plain, key, plainTextLength, keyLength):
    unitsLength = plainTextLength + keyLength
    # 定義Alice網絡
    with tf.variable_scope('Alice'):
        Alice_input = tf.concat([plain, key], axis=1)
        A_w = tf.Variable(tf.truncated_normal(shape=[unitsLength, unitsLength], mean=0, stddev=0.1))
        Alice_FC_layer = tf.nn.sigmoid(tf.matmul(Alice_input, A_w))
        Alice_output = _ConvNet(Alice_FC_layer, unitsLength)
        # print(Alice_output.shape)

    reshape_Alice_output = tf.reshape(Alice_output, shape=[-1, plainTextLength])

    # 定義Bob網絡
    with tf.variable_scope('Bob'):
        Bob_input = tf.concat([reshape_Alice_output, key], axis=1)
        B_w = tf.Variable(tf.truncated_normal(shape=[unitsLength, unitsLength], mean=0, stddev=0.1))
        Bob_FC_layer = tf.nn.sigmoid(tf.matmul(Bob_input, B_w))
        Bob_output = _ConvNet(Bob_FC_layer, unitsLength)

    # 定義Eve 網絡
    with tf.variable_scope('Eve'):
        E_w_1 = tf.Variable(tf.truncated_normal(shape=[plainTextLength, unitsLength], mean=0, stddev=0.1))
        E_FC_layer1 = tf.nn.sigmoid(tf.matmul(reshape_Alice_output, E_w_1))
        E_w_2 = tf.Variable(tf.truncated_normal(shape=[unitsLength, unitsLength], mean=0, stddev=0.1))
        E_FC_layer2 = tf.nn.sigmoid(tf.matmul(E_FC_layer1, E_w_2))
        Eve_output = _ConvNet(E_FC_layer2, unitsLength)
    # print('Alice:', Alice_output.shape, ' Bob:', Bob_output.shape, ' Eve:', Eve_output.shape)
    return Alice_output, Bob_output, Eve_output

下面的兩幅圖明文和密文的長度爲16,minibatch大小爲4096時的結果,在tensorboard中顯示,可以看出,Bob幾乎能夠正確地恢復出明文,Eve恢復明文P的錯誤位數穩定於7.6bits。

當明文長度取值32比特時,Eve恢復明文時的錯誤比特數大約穩定於15.3比特(理想應該是16bites),當明文長度取值64比特時,Eve恢復明文時的錯誤比特數大約爲30比特(理想應該是32比特), 我給出的解釋是,當輸入長度增大時,也相當於數據結構變複雜,而此時的網絡架構對於輸入數據就會顯得簡單,處於‘欠擬合’狀態。可是在 對稱密碼中,如果分組長度爲16的話,過於短小,一般爲128bits,所以可以適當增加網絡複雜度。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章