<commit_before>/* Copyright (c) 2016 <NAME>
 * Copyright (c) 2016 Trail of Bits, Inc.
 *
 * Licensed 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.
 */

import Foundation
import Security

final class SecureEnclaveKeyReference {
    let underlying: SecKey
    fileprivate init(_ underlying: SecKey) {
        self.underlying = underlying
    }
}

final class SecureEnclaveKeyData {
    let underlying: [String: Any]
    let ref: SecureEnclaveKeyReference
    let data: Data
    
    fileprivate init(_ underlying: CFDictionary) {
        let converted = underlying as! [String: Any]
        self.underlying = converted
        self.data = converted[kSecValueData as String] as! Data
        self.ref = SecureEnclaveKeyReference(converted[kSecValueRef as String] as! SecKey)
    }
    
    var hex: String {
        return self.data.map { String(format: "%02hhx", $0) }.joined()
    }
}

struct SecureEnclaveHelperError: Error {
    let message: String
    let osStatus: OSStatus?
    let link: String
    
    init(message: String, osStatus: OSStatus?) {
        self.message = message
        self.osStatus = osStatus
        if let code = osStatus {
            link = "https://www.osstatus.com/search/results?platform=all&framework=Security&search=\(code)"
        }
        else {
            link = ""
        }
    }
}

final class SecureEnclaveHelper {
    
    let publicLabel: String
    let privateLabel: String
    
    /*!
     @param publicLabel The user visible label in the device's key chain
     @param privateLabel The label used to identify the key in the secure enclave
     */
    init(publicLabel: String, privateLabel: String) {
        self.publicLabel = publicLabel
        self.privateLabel = privateLabel
    }
    
    func sign(_ digest: Data, privateKey: SecureEnclaveKeyReference) throws -> Data {
        
        let blockSize = 256
        let maxChunkSize = blockSize - 11
        guard digest.count / MemoryLayout<UInt8>.size <= maxChunkSize else {
            throw SecureEnclaveHelperError(message: "data length exceeds \(maxChunkSize)", osStatus: nil)
        }
        
        var digestBytes = [UInt8](repeating: 0, count: digest.count / MemoryLayout<UInt8>.size)
        digest.copyBytes(to: &digestBytes, count: digest.count)
        
        var signatureBytes = [UInt8](repeating: 0, count: blockSize)
        var signatureLength = blockSize
        
        let status = SecKeyRawSign(privateKey.underlying, .PKCS1, digestBytes, digestBytes.count, &signatureBytes, &signatureLength)
        guard status == errSecSuccess else {
            if status == errSecParam {
                throw SecureEnclaveHelperError(message: "Could not create signature due to bad parameters", osStatus: status)
            }
            else {
                throw SecureEnclaveHelperError(message: "Could not create signature", osStatus: status)
            }
        }
        return Data(bytes: UnsafePointer<UInt8>(signatureBytes), count: signatureLength)
    }
    
    func getPublicKey() throws -> SecureEnclaveKeyData {
        let query: [String: Any] = [
            kSecClass as String: kSecClassKey,
            kSecAttrKeyType as String: attrKeyTypeEllipticCurve,
            kSecAttrApplicationTag as String: publicLabel,
            kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
            kSecReturnData as String: true,
            kSecReturnRef as String: true,
            kSecReturnPersistentRef as String: true,
            ]
        let raw = try getSecKeyWithQuery(query)
        return SecureEnclaveKeyData(raw as! CFDictionary)
    }
    
    func getPrivateKey() throws -> SecureEnclaveKeyReference {
        let query: [String: Any] = [
            kSecClass as String: kSecClassKey,
            kSecAttrKeyClass as String: kSecAttrKeyClassPrivate,
            kSecAttrLabel as String: privateLabel,
            kSecReturnRef as String: true,
            kSecUseOperationPrompt as String: "Authenticate to sign data",
            ]
        let raw = try getSecKeyWithQuery(query)
        return SecureEnclaveKeyReference(raw as! SecKey)
    }
    
    func generateKeyPair(accessControl: SecAccessControl) throws -> (`public`: SecureEnclaveKeyReference, `private`: SecureEnclaveKeyReference) {
        
        let privateKeyParams: [String: Any] = [
            kSecAttrLabel as String: privateLabel,
            kSecAttrIsPermanent as String: true,
            kSecAttrAccessControl as String: accessControl,
            kSecAttrApplicationTag as String: privateLabel,
            ]
        
        let params: [String: Any] = [
            kSecAttrKeyType as String: attrKeyTypeEllipticCurve,
            kSecAttrKeySizeInBits as String: 256,
            kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave,
            kSecPrivateKeyAttrs as String: privateKeyParams
        ]
        
        var publicKey, privateKey: SecKey?
        let status = SecKeyGeneratePair(params as CFDictionary, &publicKey, &privateKey)
        guard status == errSecSuccess else {
            throw SecureEnclaveHelperError(message: "Could not generate keypair", osStatus: status)
        }
        return (public: SecureEnclaveKeyReference(publicKey!), private: SecureEnclaveKeyReference(privateKey!))
    }
    
    func deletePublicKey() throws {
        let query: [String: Any] = [
            kSecClass as String: kSecClassKey,
            kSecAttrKeyType as String: attrKeyTypeEllipticCurve,
            kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
            kSecAttrApplicationTag as String: publicLabel
        ]
        let status = SecItemDelete(query as CFDictionary)
        guard status == errSecSuccess else {
            throw SecureEnclaveHelperError(message: "Could not delete private key", osStatus: status)
        }
    }<commit_msg>Removing excessive query parameter<commit_after>/* Copyright (c) 2016 <NAME>
 * Copyright (c) 2016 Trail of Bits, Inc.
 *
 * Licensed 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.
 */

import Foundation
import Security

final class SecureEnclaveKeyReference {
    let underlying: SecKey
    fileprivate init(_ underlying: SecKey) {
        self.underlying = underlying
    }
}

final class SecureEnclaveKeyData {
    let underlying: [String: Any]
    let ref: SecureEnclaveKeyReference
    let data: Data
    
    fileprivate init(_ underlying: CFDictionary) {
        let converted = underlying as! [String: Any]
        self.underlying = converted
        self.data = converted[kSecValueData as String] as! Data
        self.ref = SecureEnclaveKeyReference(converted[kSecValueRef as String] as! SecKey)
    }
    
    var hex: String {
        return self.data.map { String(format: "%02hhx", $0) }.joined()
    }
}

struct SecureEnclaveHelperError: Error {
    let message: String
    let osStatus: OSStatus?
    let link: String
    
    init(message: String, osStatus: OSStatus?) {
        self.message = message
        self.osStatus = osStatus
        if let code = osStatus {
            link = "https://www.osstatus.com/search/results?platform=all&framework=Security&search=\(code)"
        }
        else {
            link = ""
        }
    }
}

final class SecureEnclaveHelper {
    
    let publicLabel: String
    let privateLabel: String
    
    /*!
     @param publicLabel The user visible label in the device's key chain
     @param privateLabel The label used to identify the key in the secure enclave
     */
    init(publicLabel: String, privateLabel: String) {
        self.publicLabel = publicLabel
        self.privateLabel = privateLabel
    }
    
    func sign(_ digest: Data, privateKey: SecureEnclaveKeyReference) throws -> Data {
        
        let blockSize = 256
        let maxChunkSize = blockSize - 11
        guard digest.count / MemoryLayout<UInt8>.size <= maxChunkSize else {
            throw SecureEnclaveHelperError(message: "data length exceeds \(maxChunkSize)", osStatus: nil)
        }
        
        var digestBytes = [UInt8](repeating: 0, count: digest.count / MemoryLayout<UInt8>.size)
        digest.copyBytes(to: &digestBytes, count: digest.count)
        
        var signatureBytes = [UInt8](repeating: 0, count: blockSize)
        var signatureLength = blockSize
        
        let status = SecKeyRawSign(privateKey.underlying, .PKCS1, digestBytes, digestBytes.count, &signatureBytes, &signatureLength)
        guard status == errSecSuccess else {
            if status == errSecParam {
                throw SecureEnclaveHelperError(message: "Could not create signature due to bad parameters", osStatus: status)
            }
            else {
                throw SecureEnclaveHelperError(message: "Could not create signature", osStatus: status)
            }
        }
        return Data(bytes: UnsafePointer<UInt8>(signatureBytes), count: signatureLength)
    }
    
    func getPublicKey() throws -> SecureEnclaveKeyData {
        let query: [String: Any] = [
            kSecClass as String: kSecClassKey,
            kSecAttrKeyType as String: attrKeyTypeEllipticCurve,
            kSecAttrApplicationTag as String: publicLabel,
            kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
            kSecReturnData as String: true,
            kSecReturnRef as String: true,
            kSecReturnPersistentRef as String: true,
            ]
        let raw = try getSecKeyWithQuery(query)
        return SecureEnclaveKeyData(raw as! CFDictionary)
    }
    
    func getPrivateKey() throws -> SecureEnclaveKeyReference {
        let query: [String: Any] = [
            kSecClass as String: kSecClassKey,
            kSecAttrKeyClass as String: kSecAttrKeyClassPrivate,
            kSecAttrLabel as String: privateLabel,
            kSecReturnRef as String: true,
            kSecUseOperationPrompt as String: "Authenticate to sign data",
            ]
        let raw = try getSecKeyWithQuery(query)
        return SecureEnclaveKeyReference(raw as! SecKey)
    }
    
    func generateKeyPair(accessControl: SecAccessControl) throws -> (`public`: SecureEnclaveKeyReference, `private`: SecureEnclaveKeyReference) {
        
        let privateKeyParams: [String: Any] = [
            kSecAttrLabel as String: privateLabel,
            kSecAttrIsPermanent as String: true,
            kSecAttrAccessControl as String: accessControl,
            ]
        
        let params: [String: Any] = [
            kSecAttrKeyType as String: attrKeyTypeEllipticCurve,
            kSecAttrKeySizeInBits as String: 256,
            kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave,
            kSecPrivateKeyAttrs as String: privateKeyParams
        ]
        
        var publicKey, privateKey: SecKey?
        let status = SecKeyGeneratePair(params as CFDictionary, &publicKey, &privateKey)
        guard status == errSecSuccess else {
            throw SecureEnclaveHelperError(message: "Could not generate keypair", osStatus: status)
        }
        return (public: SecureEnclaveKeyReference(publicKey!), private: SecureEnclaveKeyReference(privateKey!))
    }
    
    func deletePublicKey() throws {
        let query: [String: Any] = [
            kSecClass as String: kSecClassKey,
            kSecAttrKeyType as String: attrKeyTypeEllipticCurve,
            kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
            kSecAttrApplicationTag as String: publicLabel
        ]
        let status = SecItemDelete(query as CFDictionary)
        guard status == errSecSuccess else {
            throw SecureEnclaveHelperError(message: "Could not delete private key", osStatus: status)
        }
    }
{'fraction_non_alphanumeric': 0.07293239056169064, 'fraction_numerical': 0.004925717009613093, 'mean_word_length': 2.5761363636363637, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 19, 'https://': 2, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6534555', 'n_tokens_mistral': 3444, 'n_tokens_neox': 3338, 'n_words': 1019}