# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.

"""Unit tests for crypto mapping functions."""
import calendar
import datetime
import time

from pbkdf2 import PBKDF2

import mpin
import crypto


def test_today():
    """Get today."""
    today = crypto.today()
    assert type(today) == int
    assert today == calendar.timegm(time.gmtime()) / (60 * 1440)


def test_auth_success():
    """Test the basic authentication flow."""
    seed = open('/dev/urandom', 'rb').read(100)
    RNG = crypto.get_random_generator(seed)

    y = crypto.mpin_random_generate(RNG)

    date = 16238
    PIN = 1234
    mpin_id = '{"mobile": 1, "issued": "2013-10-19T06:12:28Z", "userID": "<EMAIL>", "salt": "<PASSWORD>"}'

    token = "040d73e9b7c746525edbb90e042a6b8f6e41e2417a0c0c1600f8b693c0b6c0bbce0c61b73100798c7505b3eb12c2393187355145090333f904fd896684ec4990d3".decode("hex")
    timePermit = "0408ba13483e817626a45be598b1b89296aa805a6aa31e98503321c30d7177711a1c98a77cbf8edf2497d0f5f7593c72457b36cd3e4f19e1f3c5636c0ca7a1817d".decode("hex")
    serverSecret = "0d5a0bb4621c4eec9ee28b0339047e7afdaae87c5f0f972253f2e90f55dbda4a16efc98cb3b925d4237a14527b1db361f460dae271f115c28ff5f3ef5fb5dae20a8ccc12bea3fc3f5911853eff3642e649140fcf0892a13ec8b22e94a750a2930c64f5792a22bc01580cbd041c7a8c21659abacead12fd4460f17b27f5940d4d".decode("hex")

    # Client part
    Y = mpin.ffi.new("octet*")
    Yval = mpin.ffi.new("char [%s]" % len(y), y)
    Y[0].val = Yval
    Y[0].max = len(y)
    Y[0].len = len(y)

    MPIN_ID = mpin.ffi.new("octet*")
    MPIN_IDval = mpin.ffi.new("char [%s]" % len(mpin_id), mpin_id)
    MPIN_ID[0].val = MPIN_IDval
    MPIN_ID[0].max = len(mpin_id)
    MPIN_ID[0].len = len(mpin_id)

    TOKEN = mpin.ffi.new("octet*")
    TOKENval = mpin.ffi.new("char [%s]" % len(token), token)
    TOKEN[0].val = TOKENval
    TOKEN[0].len = len(token)
    TOKEN[0].max = len(token)

    X = mpin.ffi.new("octet*")
    Xval = mpin.ffi.new("char []", mpin.PGS)
    X[0].val = Xval
    X[0].max = mpin.PGS
    X[0].len = mpin.PGS

    CLIENT_SECRET = mpin.ffi.new("octet*")
    CLIENT_SECRETval = mpin.ffi.new("char []", mpin.G1)
    CLIENT_SECRET[0].val = CLIENT_SECRETval
    CLIENT_SECRET[0].max = mpin.G1
    CLIENT_SECRET[0].len = mpin.G1

    U = mpin.ffi.new("octet*")
    Uval = mpin.ffi.new("char []", mpin.G1)
    U[0].val = Uval
    U[0].max = mpin.G1
    U[0].len = mpin.G1

    UT = mpin.ffi.new("octet*")
    UTval = mpin.ffi.new("char []", mpin.G1)
    UT[0].val = UTval
    UT[0].max = mpin.G1
    UT[0].len = mpin.G1

    TIMEPERMIT = mpin.ffi.new("octet*")
    TIMEPERMITval = mpin.ffi.new("char [%s]" % len(timePermit), timePermit)
    TIMEPERMIT[0].val = TIMEPERMITval
    TIMEPERMIT[0].len = len(timePermit)
    TIMEPERMIT[0].max = len(timePermit)

    # Client first pass
    rtn = mpin.libmpin.MPIN_CLIENT_1(date, MPIN_ID, RNG, X, PIN, TOKEN, CLIENT_SECRET, U, UT, TIMEPERMIT)
    assert rtn == 0

    # Client second pass
    rtn = mpin.libmpin.MPIN_CLIENT_2(X, Y, CLIENT_SECRET)
    assert rtn == 0

    # Server second pass
    hid, htid = crypto.mpin_server_1(mpin_id, date)
    success_code, _, _ = crypto.mpin_server_2(
        serverSecret,
        mpin.toHex(CLIENT_SECRET).decode('hex'),
        date, hid, htid,
        mpin.toHex(Y).decode('hex'),
        mpin.toHex(U).decode('hex'),
        mpin.toHex(UT).decode('hex'))
    assert success_code == 0


def test_encrypt_decrypt_master_secret():
    """Test encryption/decription of master secret."""
    seed = open('/dev/urandom', 'rb').read(100)

    rng = crypto.get_random_generator(seed)
    now = datetime.datetime.now()

    aes_key = PBKDF2('passphrase', 'salt').read(16)

    ciphertext_hex, iv_hex, tag_hex = crypto.aes_gcm_encrypt('master_secret', aes_key, rng, now.strftime('%Y-%m-%dT%H:%M:%SZ'))

    tag, plaintext = crypto.aes_gcm_decrypt(
        aes_key=aes_key,
        iv=str(iv_hex.decode('hex')),
        header=str(now.strftime('%Y-%m-%dT%H:%M:%SZ')),
        ciphertext=str(ciphertext_hex.decode('hex')))

    assert tag == tag_hex
    assert plaintext.decode('hex') == 'master_secret'
