// Header.swift
// This file is part of KeePassKit.
//
// Copyright © 2019 Maxime Epain. All rights reserved.
//
// KeePassKit is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// KeePassKit is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with KeePassKit. If not, see <https://www.gnu.org/licenses/>.

import Foundation
import Binary
import Crypto

struct Header {
    let cipher: Flag
    let version: UInt32
    let masterSeed: Bytes
    let initialVector: Bytes
    let groups: UInt32
    let entries: UInt32
    let contentHash: Bytes
    let transformSeed: Bytes
    let transformRounds: UInt32
}

struct Flag: OptionSet, Streamable {
    let rawValue: UInt32

    static let sha2     = Flag(rawValue: 1<<0)
    static let aes      = Flag(rawValue: 1<<1)
    static let arc4     = Flag(rawValue: 1<<2)
    static let twofish  = Flag(rawValue: 1<<3)
}

extension Header: Streamable {

    init(from input: Input) throws {
        cipher = try input.read()
        version = try input.read()
        masterSeed = try input.read(lenght: 16)
        initialVector = try input.read(lenght: 16)
        groups = try input.read()
        entries = try input.read()
        contentHash = try input.read(lenght: 32)
        transformSeed = try input.read(lenght: 32)
        transformRounds = try input.read()
    }

    func write(to output: Output) throws {
        try output.write(cipher)
        try output.write(version)
        try output.write(masterSeed)
        try output.write(initialVector)
        try output.write(groups)
        try output.write(entries)
        try output.write(contentHash)
        try output.write(transformSeed)
        try output.write(transformRounds)
    }

}

extension Header {

    func masterKey(from compositeKey: CompositeKey) throws -> Bytes {
        let key = try compositeKey.serialize()

        // Key Derivation
        let kdf = try AESKeyDerivation(seed: transformSeed, rounds: UInt64(transformRounds))
        let derivedKey = try kdf.derive(key: key)

        return SHA256.hash( masterSeed + derivedKey )
    }
}
