/*
 * Copyright (c) 2017 Apple Inc. All Rights Reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_LICENSE_HEADER_END@
 */

#if OCTAGON
#import <Foundation/Foundation.h>
#import "OTBottledPeer.h"
#import "OTBottledPeerSigned.h"
#import "OTIdentity.h"

#import <SecurityFoundation/SFEncryptionOperation.h>
#import <SecurityFoundation/SFSigningOperation.h>
#import <SecurityFoundation/SFDigestOperation.h>
#import <SecurityFoundation/SFKey.h>
#import <SecurityFoundation/SFKey_Private.h>

#import <corecrypto/cchkdf.h>
#import <corecrypto/ccsha2.h>
#import <corecrypto/ccec.h>

#include <utilities/debugging.h>

@interface OTBottledPeerSigned ()
@property (nonatomic, strong) OTBottledPeer*  bp;
@property (nonatomic, strong) NSData*         signatureUsingEscrowKey;
@property (nonatomic, strong) NSData*         signatureUsingPeerKey;
@property (nonatomic, strong) NSData*         escrowSigningPublicKey;
@end

@implementation OTBottledPeerSigned

// Create signatures
- (nullable instancetype) initWithBottledPeer:(OTBottledPeer*)bp
                           escrowedSigningKey:(SFECKeyPair *)escrowedSigningKey
                               peerSigningKey:(SFECKeyPair *)peerSigningKey
                                        error:(NSError**)error
{
    self = [super init];
    if (self) {
        _bp = bp;
        _escrowSigningSPKI = [escrowedSigningKey.publicKey encodeSubjectPublicKeyInfo];
        SFEC_X962SigningOperation* xso = [OTBottledPeerSigned signingOperation];
        _signatureUsingEscrowKey = [xso sign:bp.data withKey:escrowedSigningKey error:error].signature;
        if (!_signatureUsingEscrowKey) {
            return nil;
        }
        _signatureUsingPeerKey = [xso sign:bp.data withKey:peerSigningKey error:error].signature;
        if (!_signatureUsingPeerKey) {
            return nil;
        }
    }
    return self;
}

-(NSString*) escrowSigningPublicKeyHash
{
    const struct ccdigest_info *di = ccsha384_di();
    NSMutableData* result = [[NSMutableData alloc] initWithLength:ccsha384_di()->output_size];

    ccdigest(di, [self.escrowSigningPublicKey length], [self.escrowSigningPublicKey bytes], [result mutableBytes]);

    return [result base64EncodedStringWithOptions:0];
}

// Verify signatures, or return nil
- (nullable instancetype) initWithBottledPeer:(OTBottledPeer*)bp
                         signatureUsingEscrow:(NSData*)signatureUsingEscrow
                        signatureUsingPeerKey:(NSData*)signatureUsingPeerKey
                        escrowedSigningPubKey:(SFECPublicKey *)escrowedSigningPubKey
                                        error:(NSError**)error
{
    self = [super init];
    if (self) {
        _bp = bp;
        _escrowSigningSPKI = [escrowedSigningPubKey encodeSubjectPublicKeyInfo];
        _signatureUsingPeerKey = signatureUsingPeerKey;
        _signatureUsingEscrowKey = signatureUsingEscrow;
        _escrowSigningPublicKey = [escrowedSigningPubKey keyData];

        SFEC_X962SigningOperation* xso = [OTBottledPeerSigned signingOperation];

        SFSignedData *escrowSigned = [[SFSignedData alloc] initWithData:bp.data signature:signatureUsingEscrow];
        if (![xso verify:escrowSigned withKey:escrowedSigningPubKey error:error]) {
            return nil;
        }
        SFSignedData *peerSigned = [[SFSignedData alloc] initWithData:bp.data signature:signatureUsingPeerKey];
        if (![xso verify:peerSigned withKey:bp.peerSigningKey.publicKey error:error]) {
            return nil;
        }
        //stuff restored keys in the keychain
        [OTIdentity storeOctagonIdentityIntoKeychain:self.bp.peerSigningKey restoredEncryptionKey:self.bp.peerEncryptionKey escrowSigningPubKeyHash:self.escrowSigningPublicKeyHash restoredPeerID:self.bp.spID error:error];
    }
    return self;
}

+ (SFEC_X962SigningOperation*) signingOperation
{
    SFECKeySpecifier *keySpecifier = [[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384];
    id<SFDigestOperation> digestOperation = [[SFSHA384DigestOperation alloc] init];
    return [[SFEC_X962SigningOperation alloc] initWithKeySpecifier:keySpecifier digestOperation:digestOperation];
}

+ (BOOL) verifyBottleSignature:(NSData*)data signature:(NSData*)signature key:(_SFECPublicKey*) pubKey error:(NSError**)error
{
    SFEC_X962SigningOperation* xso = [OTBottledPeerSigned signingOperation];

    SFSignedData *peerSigned = [[SFSignedData alloc] initWithData:data signature:signature];
    
    return ([xso verify:peerSigned withKey:pubKey error:error] != nil);

}

- (nullable instancetype) initWithBottledPeerRecord:(OTBottledPeerRecord *)record
                                         escrowKeys:(OTEscrowKeys *)escrowKeys
                                              error:(NSError**)error
{
    OTBottledPeer *bp = [[OTBottledPeer alloc] initWithData:record.bottle
                                                 escrowKeys:escrowKeys
                                                      error:error];
    if (!bp) {
        return nil;
    }
    return [self initWithBottledPeer:bp
                signatureUsingEscrow:record.signatureUsingEscrowKey
               signatureUsingPeerKey:record.signatureUsingPeerKey
               escrowedSigningPubKey:escrowKeys.signingKey.publicKey
                               error:error];
}

- (OTBottledPeerRecord *)asRecord:(NSString*)escrowRecordID
{
    OTBottledPeerRecord *rec = [[OTBottledPeerRecord alloc] init];
    rec.spID = self.bp.spID;
    rec.escrowRecordID = [escrowRecordID copy];
    rec.peerSigningSPKI = [self.bp.peerSigningKey.publicKey encodeSubjectPublicKeyInfo];
    rec.escrowedSigningSPKI = self.escrowSigningSPKI;
    rec.bottle = self.bp.data;
    rec.signatureUsingPeerKey = self.signatureUsingPeerKey;
    rec.signatureUsingEscrowKey = self.signatureUsingEscrowKey;
    rec.launched = @"NO";
    return rec;
}

@end
#endif

