Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
signalapp
GitHub Repository: signalapp/Signal-iOS
Path: blob/main/SignalServiceKit/Cryptography/ECKeyPair.swift
1 views
//
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//

import Foundation
public import LibSignalClient

@objc(ECKeyPair)
public final class ECKeyPair: NSObject, NSSecureCoding {
    public let keyPair: IdentityKeyPair
    public var identityKeyPair: IdentityKeyPair { keyPair }

    init(_ keyPair: IdentityKeyPair) {
        self.keyPair = keyPair
    }

    /**
     * Build a keypair from existing key data.
     * If you need a *new* keypair, user `ECKeyPair.generateKeyPair` instead.
     */
    convenience init(publicKeyData: Data, privateKeyData: Data) throws {
        let publicKey = try PublicKey(keyData: publicKeyData)
        let privateKey = try PrivateKey(privateKeyData)

        self.init(IdentityKeyPair(publicKey: publicKey, privateKey: privateKey))
    }

    private static let TSECKeyPairPublicKey = "TSECKeyPairPublicKey"
    private static let TSECKeyPairPrivateKey = "TSECKeyPairPrivateKey"

    public convenience init?(coder: NSCoder) {
        var returnedLength = 0
        let publicKeyBuffer = coder.decodeBytes(forKey: Self.TSECKeyPairPublicKey, returnedLength: &returnedLength)
        guard let publicKeyBuffer else {
            return nil
        }
        let publicKeyData = Data(bytes: publicKeyBuffer, count: returnedLength)

        returnedLength = 0
        let privateKeyBuffer = coder.decodeBytes(forKey: Self.TSECKeyPairPrivateKey, returnedLength: &returnedLength)
        guard let privateKeyBuffer else {
            return nil
        }
        let privateKeyData = Data(bytes: privateKeyBuffer, count: returnedLength)

        do {
            try self.init(publicKeyData: publicKeyData, privateKeyData: privateKeyData)
        } catch {
            Logger.warn("\(error)")
            return nil
        }
    }

    public func encode(with coder: NSCoder) {
        self.identityKeyPair.publicKey.keyBytes.withUnsafeBytes {
            coder.encodeBytes($0.baseAddress, length: $0.count, forKey: Self.TSECKeyPairPublicKey)
        }
        self.identityKeyPair.privateKey.serialize().withUnsafeBytes {
            coder.encodeBytes($0.baseAddress, length: $0.count, forKey: Self.TSECKeyPairPrivateKey)
        }
    }

    public class var supportsSecureCoding: Bool {
        return true
    }

    public static func generateKeyPair() -> ECKeyPair {
        return ECKeyPair(IdentityKeyPair.generate())
    }

    private func sign(_ data: Data) throws -> Data {
        return identityKeyPair.privateKey.generateSignature(message: data)
    }

    public var publicKey: Data {
        return identityKeyPair.publicKey.keyBytes
    }

    public var privateKey: Data {
        return identityKeyPair.privateKey.serialize()
    }
}

extension IdentityKeyPair {
    public var asECKeyPair: ECKeyPair { return .init(self) }
}