All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.guardtime.ksi.tree.BlindingMaskLinkingHashTreeBuilder Maven / Gradle / Ivy

/*
 * Copyright 2013-2019 Guardtime, Inc.
 *
 *  This file is part of the Guardtime client SDK.
 *
 *  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, CONDITIONS, OR OTHER LICENSES OF ANY KIND, either
 *  express or implied. See the License for the specific language governing
 *  permissions and limitations under the License.
 *  "Guardtime" and "KSI" are trademarks or registered trademarks of
 *  Guardtime, Inc., and no license to trademarks is granted; Guardtime
 *  reserves and retains all trademark rights.
 *
 */

package com.guardtime.ksi.tree;

import com.guardtime.ksi.blocksigner.IdentityMetadata;
import com.guardtime.ksi.exceptions.KSIException;
import com.guardtime.ksi.hashing.DataHash;
import com.guardtime.ksi.hashing.DataHasher;
import com.guardtime.ksi.hashing.HashAlgorithm;
import com.guardtime.ksi.util.Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;

/**
 * Hash tree (aka Merkle tree) builder implementation using blinding masks.
 *
 * 

* Background: * A strong hash function can’t be directly reversed to learn the input value from which the hash value * in the chain was created. However, a typical log record may contain insufficient entropy to make that argument — an * attacker who knows the pattern of the input could exhaustively test all possible variants to find the one that yields * the hash value actually in the chain and thus learn the contents of the record. To prevent this kind of informed * brute-force attack, a blinding mask with sufficient entropy can be added to each record before aggregating the hash * values. (Source: https://www.researchgate.net/profile/Ahto_Truu/publication/290563005_Efficient_Record-Level_Keyless_Signatures_for_Audit_Logs/links/58b96d1092851c471d4a5888/Efficient-Record-Level-Keyless-Signatures-for-Audit-Logs.pdf page 3) * *

* {@link BlindingMaskLinkingHashTreeBuilder} does not support {@link IdentityMetadata} aggregation and methods * {@link BlindingMaskLinkingHashTreeBuilder#add(ImprintNode, IdentityMetadata)} and * {@link BlindingMaskLinkingHashTreeBuilder#calculateHeight(ImprintNode, IdentityMetadata)} will throw an * {@link UnsupportedOperationException} exception. * *

* This builder can not be used multiple times and it is not thread safe. */ public class BlindingMaskLinkingHashTreeBuilder implements TreeBuilder { private static final Logger logger = LoggerFactory.getLogger(BlindingMaskLinkingHashTreeBuilder.class); protected static final long MASKED_NODE_LEVEL = 1; private final TreeBuilder treeBuilder; private final byte[] initializationVector; private final HashAlgorithm hashAlgorithm; private DataHash previousBlockHash; /** * Creates an instance of {@link BlindingMaskLinkingHashTreeBuilder} using a * {@link com.guardtime.ksi.tree.Util#DEFAULT_AGGREGATION_ALGORITHM} hash algorithm and a zero hash value as * previous block hash. * * @param initializationVector initialization vector used to calculate masking nodes, must not be null. The length * of the initialization vector should be as long as the output of the * {@link com.guardtime.ksi.tree.Util#DEFAULT_AGGREGATION_ALGORITHM} hash algorithm. * @throws IllegalArgumentException if initializationVector length is not as long as the output of the * {@link com.guardtime.ksi.tree.Util#DEFAULT_AGGREGATION_ALGORITHM} hash * algorithm. * @throws NullPointerException if one of the required input parameters is null. */ public BlindingMaskLinkingHashTreeBuilder(byte[] initializationVector) { this(com.guardtime.ksi.tree.Util.DEFAULT_AGGREGATION_ALGORITHM, initializationVector, new DataHash(com.guardtime.ksi.tree.Util.DEFAULT_AGGREGATION_ALGORITHM, new byte[com.guardtime.ksi.tree.Util.DEFAULT_AGGREGATION_ALGORITHM.getLength()])); } /** * Creates an instance of {@link BlindingMaskLinkingHashTreeBuilder} using * {@link com.guardtime.ksi.tree.Util#DEFAULT_AGGREGATION_ALGORITHM} hash algorithm and a {@link DataHash} from * previous block. * * @param previousBlockHash previous block data hash used to calculate first blinding mask, must not be null. * @param initializationVector initialization vector used to calculate masking nodes, must not be null. The length * of the initialization vector should be as long as the output of the * {@link com.guardtime.ksi.tree.Util#DEFAULT_AGGREGATION_ALGORITHM} hash algorithm. * @throws IllegalArgumentException if initializationVector length is not as long as the output of the * {@link com.guardtime.ksi.tree.Util#DEFAULT_AGGREGATION_ALGORITHM} hash algorithm. * @throws NullPointerException if one of the required input parameters is null. */ public BlindingMaskLinkingHashTreeBuilder(byte[] initializationVector, DataHash previousBlockHash) { this(com.guardtime.ksi.tree.Util.DEFAULT_AGGREGATION_ALGORITHM, initializationVector, previousBlockHash); } /** * Creates an instance of {@link BlindingMaskLinkingHashTreeBuilder}. * * @param algorithm hash algorithm used to calculate inner nodes of the hash tree, must not be null. * @param initializationVector initialization vector used to calculate masking nodes, must not be null. The length * of the initialization vector should be as long as the output of the hash * {@code algorithm}. * @param previousBlockHash previous block data hash used to calculate first blinding mask, must not be null. * @throws IllegalArgumentException if initializationVector length is not as long as the output of the * {@code algorithm} hash algorithm. * @throws NullPointerException if one of the required input parameters is null. */ public BlindingMaskLinkingHashTreeBuilder(HashAlgorithm algorithm, byte[] initializationVector, DataHash previousBlockHash) { this(algorithm, initializationVector, previousBlockHash, new HashTreeBuilder(algorithm)); } /** * Creates an instance of {@link BlindingMaskLinkingHashTreeBuilder}. * * @param algorithm hash algorithm used to calculate inner nodes of the hash tree, must not be null. * @param initializationVector initialization vector used to calculate masking nodes, must not be null. The length * of the initialization vector should be as long as the output of the hash * {@code algorithm}. * @param previousBlockHash previous block data hash used to calculate first blinding mask, must not be null. * @param treeBuilder implementation of {@link TreeBuilder} to use when building the tree. * @throws IllegalArgumentException if initializationVector length is not as long as the output of the * {@code algorithm} hash algorithm. * @throws NullPointerException if one of the required input parameters is null. */ public BlindingMaskLinkingHashTreeBuilder(HashAlgorithm algorithm, byte[] initializationVector, DataHash previousBlockHash, TreeBuilder treeBuilder) { Util.notNull(algorithm, "HashAlgorithm"); Util.notNull(initializationVector, "Initialization vector"); Util.notNull(previousBlockHash, "Previous block hash"); Util.notNull(treeBuilder, "Tree builder"); if (initializationVector.length < algorithm.getLength()) { logger.warn("Initialization vector is shorter than the output of the hash algorithm."); } this.hashAlgorithm = algorithm; this.initializationVector = Arrays.copyOf(initializationVector, initializationVector.length); this.previousBlockHash = previousBlockHash; this.treeBuilder = treeBuilder; } /** * Adds a new node to the tree. * * @param node a leaf to add to the tree, must not be null. The level of the node must be 0. * @throws IllegalArgumentException if node level is greater than 0. */ @Override public void add(ImprintNode node) throws KSIException { Util.notNull(node, "Node"); if (node.getLevel() != 0) { throw new IllegalArgumentException("ImprintNode with level greater than 0 is not supported by BlindingMaskLinkingHashTreeBuilder"); } ImprintNode newNode = calculateNewNode(node); treeBuilder.add(newNode); previousBlockHash = new DataHash(newNode.getValue()); } /** * {@link IdentityMetadata} isn't supported by {@link BlindingMaskLinkingHashTreeBuilder} and this method always * throws an {@link UnsupportedOperationException} exception. */ @Override public void add(ImprintNode node, IdentityMetadata metadata) { throw new UnsupportedOperationException("Identity metadata is not supported by BlindingMaskLinkingHashTreeBuilder"); } /** * Calculates the binary tree height if new leaf would be added. * * @param node a leaf to be added to the tree, must not be null. The level of the node must be 0. * @return Hash tree height. * @throws IllegalArgumentException if node level is greater than 0. */ @Override public long calculateHeight(ImprintNode node) throws KSIException { Util.notNull(node, "Node"); return treeBuilder.calculateHeight(calculateNewNode(node)); } /** * {@link IdentityMetadata} isn't supported by {@link BlindingMaskLinkingHashTreeBuilder}. This method always * throws an {@link UnsupportedOperationException} exception. */ @Override public long calculateHeight(ImprintNode node, IdentityMetadata metadata) { throw new UnsupportedOperationException("Identity metadata is not supported by BlindingMaskLinkingHashTreeBuilder"); } /** * Adds a new list of leaves to the binary tree. * * @param nodes a list of leaves to be added to the tree, must not be null. * @throws IllegalArgumentException if node level is greater than 0. **/ @Override public void add(ImprintNode... nodes) throws KSIException { Util.notNull(nodes, "Nodes"); for (ImprintNode node : nodes) { add(node); } } /** * Builds the binary tree and returns the root hash of the tree. */ @Override public ImprintNode build() throws KSIException { return treeBuilder.build(); } protected ImprintNode calculateNewNode(ImprintNode node) { ImprintNode mask = calculateBlindingMaskNode(); DataHash newLeafNodeDataHash = com.guardtime.ksi.tree.Util.hash( hashAlgorithm, mask.getValue(), node.getValue(), MASKED_NODE_LEVEL); return new ImprintNode(mask, node, newLeafNodeDataHash, MASKED_NODE_LEVEL); } protected ImprintNode calculateBlindingMaskNode() { DataHasher hasher = new DataHasher(hashAlgorithm); hasher.addData(previousBlockHash.getImprint()).addData(initializationVector); return new ImprintNode(hasher.getHash()); } /** * Returns the last leaf hash of this block/tree (for linking next block/tree). */ public DataHash getLastNodeHash() { return previousBlockHash; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy