Skip to content

Commit

Permalink
Add PCLI command to sign account balance files (#6264)
Browse files Browse the repository at this point in the history
Signed-off-by: Iris Simon <iris.simon@swirldslabs.com>
Signed-off-by: Lev Povolotsky <lev@swirldslabs.com>
Signed-off-by: Lev Povolotsky <16233475+povolev15@users.noreply.github.com>
Co-authored-by: Lev Povolotsky <lev@swirldslabs.com>
Co-authored-by: Lev Povolotsky <16233475+povolev15@users.noreply.github.com>
Co-authored-by: Quan Nguyen <quan@swirldslabs.com>
  • Loading branch information
4 people committed Apr 28, 2023
1 parent dfba915 commit 1a0303f
Show file tree
Hide file tree
Showing 8 changed files with 291 additions and 1 deletion.
1 change: 1 addition & 0 deletions hedera-node/cli-clients/build.gradle.kts
Expand Up @@ -33,6 +33,7 @@ configurations.all {
}

dependencies {
implementation(project(mapOf("path" to ":hedera-node:hapi-utils")))
compileOnly(libs.spotbugs.annotations)
implementation(libs.bundles.swirlds)
implementation(project(":hedera-node:hedera-mono-service"))
Expand Down
@@ -0,0 +1,35 @@
/*
* Copyright (C) 2016-2023 Hedera Hashgraph, LLC
*
* 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.
*/

package com.hedera.services.cli.sign;

import com.swirlds.cli.PlatformCli;
import com.swirlds.cli.utility.AbstractCommand;
import com.swirlds.cli.utility.SubcommandOf;
import picocli.CommandLine;

/**
* A subcommand of the {@link PlatformCli}, for account balance files
*/
@CommandLine.Command(
name = "account-balance",
mixinStandardHelpOptions = true,
description = "Operations on account balance files.")
@SubcommandOf(PlatformCli.class)
public final class AccountBalanceCommand extends AbstractCommand {

private AccountBalanceCommand() {}
}
@@ -0,0 +1,49 @@
/*
* Copyright (C) 2023 Hedera Hashgraph, LLC
*
* 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.
*/

package com.hedera.services.cli.sign;

import com.swirlds.cli.utility.SubcommandOf;
import com.swirlds.platform.cli.SignCommand;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.nio.file.Path;
import java.security.KeyPair;
import picocli.CommandLine;

/**
* A subcommand of the {@link SignCommand}, for signing account balance files
*/
@CommandLine.Command(name = "sign", mixinStandardHelpOptions = true, description = "Sign account balance files")
@SubcommandOf(AccountBalanceCommand.class)
public final class AccountBalanceSignCommand extends SignCommand {
/**
* {@inheritDoc}
*/
@Override
public boolean generateSignatureFile(
@NonNull Path signatureFileDestination, @NonNull Path fileToSign, @NonNull KeyPair keyPair) {

return AccountBalanceSigningUtils.signAccountBalanceFile(signatureFileDestination, fileToSign, keyPair);
}

/**
* {@inheritDoc}
*/
@Override
public boolean isFileSupported(@NonNull final Path path) {
return AccountBalanceType.getInstance().isCorrectFile(path.toFile().getName());
}
}
@@ -0,0 +1,98 @@
/*
* Copyright (C) 2023 Hedera Hashgraph, LLC
*
* 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.
*/

package com.hedera.services.cli.sign;

import static com.hedera.services.cli.sign.SignUtils.TYPE_FILE_HASH;
import static com.hedera.services.cli.sign.SignUtils.TYPE_SIGNATURE;
import static com.hedera.services.cli.sign.SignUtils.integerToBytes;
import static com.swirlds.common.stream.LinkedObjectStreamUtilities.computeEntireHash;
import static com.swirlds.platform.util.FileSigningUtils.signData;

import com.swirlds.common.crypto.Hash;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Path;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
import java.util.Objects;

/**
* Utility class for signing account balance files
*/
public class AccountBalanceSigningUtils {

/**
* Hidden constructor
*/
private AccountBalanceSigningUtils() {}

/**
* Generates a signature file for the account balance file
*
* @param signatureFileDestination the full path where the signature file will be generated
* @param streamFileToSign the stream file to be signed
* @param keyPair the keyPair used for signing
* @return true if the signature file was generated successfully, false otherwise
*/
public static boolean signAccountBalanceFile(
@NonNull final Path signatureFileDestination,
@NonNull final Path streamFileToSign,
@NonNull final KeyPair keyPair) {

Objects.requireNonNull(signatureFileDestination, "signatureFileDestination must not be null");
Objects.requireNonNull(streamFileToSign, "streamFileToSign must not be null");
Objects.requireNonNull(keyPair, "keyPair must not be null");

try {
final Hash entireHash = computeEntireHash(streamFileToSign.toFile());
final byte[] fileHashByte = entireHash.getValue();
final byte[] signature = signData(fileHashByte, keyPair);

generateSigBalanceFile(signatureFileDestination.toFile(), signature, fileHashByte);

System.out.println("Generated signature file: " + signatureFileDestination);

return true;
} catch (final SignatureException | IOException e) {
System.err.println("Failed to sign file " + streamFileToSign.getFileName() + ". Exception: " + e);
return false;
} catch (final InvalidKeyException | NoSuchAlgorithmException | NoSuchProviderException e) {
System.err.println("Irrecoverable error encountered: " + e);
throw new RuntimeException("Irrecoverable error encountered", e);
}
}

private static void generateSigBalanceFile(final File filePath, final byte[] signature, final byte[] fileHash) {
try (final BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(filePath))) {
output.write(TYPE_FILE_HASH);
output.write(fileHash);
output.write(TYPE_SIGNATURE);
output.write(integerToBytes(signature.length));
output.write(signature);
output.flush();
} catch (final IOException e) {
System.err.println("generateSigBalanceFile :: Fail to generate signature file for " + filePath
+ " with exception :" + e);
}
}
}
@@ -0,0 +1,62 @@
/*
* Copyright (C) 2020-2023 Hedera Hashgraph, LLC
*
* 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.
*/

package com.hedera.services.cli.sign;

/**
* Contains properties related to Account Balance file type;
* Its constructor is private. Users need to use the singleton to denote this type
*/
public final class AccountBalanceType {
/**
* description of the streamType, used for logging
*/
private static final String ACCOUNT_BALANCE_DESCRIPTION = "account balance";
/**
* file name extension
*/
private static final String ACCOUNT_BALANCE_EXTENSION = "pb";
/**
* file name extension of signature file
*/
private static final String ACCOUNT_BALANCE_SIG_EXTENSION = "pb_sig";
/**
* a singleton denotes AccountBalanceType
*/
private static final AccountBalanceType INSTANCE = new AccountBalanceType();

private AccountBalanceType() {}

public static AccountBalanceType getInstance() {
return INSTANCE;
}

public String getDescription() {
return ACCOUNT_BALANCE_DESCRIPTION;
}

public String getExtension() {
return ACCOUNT_BALANCE_EXTENSION;
}

public String getSigExtension() {
return ACCOUNT_BALANCE_SIG_EXTENSION;
}

public boolean isCorrectFile(final String fileName) {
return fileName.endsWith(ACCOUNT_BALANCE_EXTENSION);
}
}
@@ -0,0 +1,40 @@
/*
* Copyright (C) 2023 Hedera Hashgraph, LLC
*
* 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.
*/

package com.hedera.services.cli.sign;

import java.nio.ByteBuffer;

public class SignUtils {
/**
* next bytes are signature
*/
public static final byte TYPE_SIGNATURE = 3;
/**
* next 48 bytes are hash384 of content of the file to be signed
*/
public static final byte TYPE_FILE_HASH = 4;

private static final int BYTES_COUNT_IN_INT = 4;

private SignUtils() {}

public static byte[] integerToBytes(final int number) {
final ByteBuffer b = ByteBuffer.allocate(BYTES_COUNT_IN_INT);
b.putInt(number);
return b.array();
}
}
5 changes: 5 additions & 0 deletions hedera-node/cli-clients/src/main/java/module-info.java
@@ -1,5 +1,6 @@
module com.hedera.services.cli {
exports com.hedera.services.cli;
exports com.hedera.services.cli.sign;

requires static com.github.spotbugs.annotations;
requires com.hedera.node.app.service.mono;
Expand All @@ -8,4 +9,8 @@
requires com.swirlds.platform;
requires com.swirlds.common;
requires info.picocli;
requires com.hedera.hashgraph.protobuf.java.api;
requires org.apache.commons.lang3;
requires com.hedera.node.app.hapi.utils;
requires com.google.protobuf;
}
2 changes: 1 addition & 1 deletion settings.gradle.kts
Expand Up @@ -144,7 +144,7 @@ dependencyResolutionManagement {
version("netty-version", "4.1.66.Final")
version("protobuf-java-version", "3.19.4")
version("slf4j-version", "2.0.3")
version("swirlds-version", "0.38.0-adhoc.x11d2d789")
version("swirlds-version", "0.38.0-adhoc.xb74a8ba8")
version("tuweni-version", "2.2.0")
version("jna-version", "5.12.1")
version("jsr305-version", "3.0.2")
Expand Down

0 comments on commit 1a0303f

Please sign in to comment.