Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Supranational BLS implementation #2453

Merged
merged 101 commits into from
Jul 31, 2020
Merged
Show file tree
Hide file tree
Changes from 97 commits
Commits
Show all changes
101 commits
Select commit Hold shift + click to select a range
fc11c9f
Draft initial experiments
Nashatyrev Jun 25, 2020
1532577
Merge remote-tracking branch 'pegasys/master' into experimental-supra
Nashatyrev Jul 3, 2020
745ca3a
Add JNA library draft
Nashatyrev Jul 7, 2020
f442a69
Move SWIG wrappers to a separate package
Nashatyrev Jul 7, 2020
986c4a8
Updated SWIG wrappers
Nashatyrev Jul 9, 2020
3eff96d
Initial BLS Blst classes. Tests updated
Nashatyrev Jul 9, 2020
7413682
Merge remote-tracking branch 'pegasys/master' into experimental-supra
Nashatyrev Jul 10, 2020
5cb89b4
Update the draft
Nashatyrev Jul 10, 2020
54f7a01
Merge remote-tracking branch 'pegasys/master' into experimental-supra
Nashatyrev Jul 15, 2020
9256c93
temp commit
Nashatyrev Jul 15, 2020
67b1a9d
Upgrade blst classes
Nashatyrev Jul 16, 2020
f8da728
Update tests
Nashatyrev Jul 16, 2020
a7439d5
Update dll
Nashatyrev Jul 16, 2020
939e301
Temp disable json-simple lib
Nashatyrev Jul 16, 2020
7fd531b
Initial draft for BLS interfaces
Nashatyrev Jul 16, 2020
69300d6
exclude outdated junit transitive dependency from json-simple module
Nashatyrev Jul 16, 2020
8724e7f
Rename test file to Win compatible
Nashatyrev Jul 16, 2020
84691cd
Merge branch 'fix-bls-tests-1' into feature-bls-interfaces
Nashatyrev Jul 16, 2020
a8bafde
Another portion of draft changes
Nashatyrev Jul 17, 2020
b5762f4
Adjust bls tests by separating them to abstract and implementation sp…
Nashatyrev Jul 17, 2020
27aace7
Add NoopBLS12381 implementation
Nashatyrev Jul 17, 2020
5f93ac4
Fix warnings
Nashatyrev Jul 17, 2020
7c4faf6
Fix more warns
Nashatyrev Jul 17, 2020
c221980
Merge remote-tracking branch 'pegasys/master' into feature-bls-interf…
Nashatyrev Jul 17, 2020
8c41c8d
Resolve merge conflicts
Nashatyrev Jul 17, 2020
aab091f
Fix the test
Nashatyrev Jul 17, 2020
2040313
Fix the test
Nashatyrev Jul 20, 2020
69313d7
Merge remote-tracking branch 'pegasys/master' into feature-bls-interf…
Nashatyrev Jul 20, 2020
59e59c1
Merge branch 'feature-bls-interfaces' into experimental-supra
Nashatyrev Jul 20, 2020
5b48be7
Remove JNA binding
Nashatyrev Jul 20, 2020
6c4314b
Move Blst code to the appropriate package, rename classes
Nashatyrev Jul 20, 2020
3c653a5
Derive Blst classes from common impl interfaces
Nashatyrev Jul 20, 2020
edf288a
Merge remote-tracking branch 'pegasys/master' into feature-blst
Nashatyrev Jul 21, 2020
a6aafa1
Load native library from resources
Nashatyrev Jul 21, 2020
763bdde
BlstSecretKey should be created from 48 bytes and return 48 bytes fro…
Nashatyrev Jul 21, 2020
950622b
Make Mikuli specific test relying on the mikuli implementation
Nashatyrev Jul 21, 2020
c20d6cb
Merge remote-tracking branch 'pegasys/master' into feature-blst
Nashatyrev Jul 21, 2020
d32eed8
Add latest Blst DLL
Nashatyrev Jul 21, 2020
28f099a
Merge remote-tracking branch 'pegasys/master' into feature-blst
Nashatyrev Jul 22, 2020
cc7f83b
BlstSecretKey is created from Bytes32
Nashatyrev Jul 22, 2020
27f8cb3
Fix Mikuli specific test to use Mikuli implementation
Nashatyrev Jul 22, 2020
f06b765
Implement BlstPublicKey validation
Nashatyrev Jul 22, 2020
922254f
Replace BLSPublicKey.isValid() with fromBytesCompressedValidate() method
Nashatyrev Jul 22, 2020
dd744d1
Make BLSPublicKey fromBytesCompressed/toBytesCompressed operating wit…
Nashatyrev Jul 22, 2020
dcef1b3
Rename BLSPublicKey/BLSSignature.from/toBytes() methods to from/toSSZ…
Nashatyrev Jul 22, 2020
a8ae35b
Hide constructors. Make PublicKey and Signature implementations to be…
Nashatyrev Jul 22, 2020
3e13b6e
Fix DLL binary attribute
Nashatyrev Jul 22, 2020
2f4d590
Again add DLL
Nashatyrev Jul 22, 2020
706f888
Validate BlstSignature when creating from bytes
Nashatyrev Jul 22, 2020
66a854c
Add comments
Nashatyrev Jul 22, 2020
4db7dc4
Fix BlstPublicKey validation
Nashatyrev Jul 22, 2020
14e7f97
Fix accidentally committed wrong ETH2_DST
Nashatyrev Jul 23, 2020
1b2f04b
Add a BLS test
Nashatyrev Jul 23, 2020
54c6d4e
Add mikuli and blst specific infinity signature tests
Nashatyrev Jul 23, 2020
d65ec86
Add BlstPublicKey.fromBytesUncompressed for testing
Nashatyrev Jul 23, 2020
87d4cd5
Convert pre-generated keypairs dump to 32-byte secretKeys and compres…
Nashatyrev Jul 23, 2020
d4e5ca8
For BLSPublicKey/BLSSignature use bytes representation for hashCode()…
Nashatyrev Jul 23, 2020
5b308dd
Move the test case to BLSSignatureTest since empty() case is not hand…
Nashatyrev Jul 23, 2020
1af5520
Apply spotless. Remove obsolete file
Nashatyrev Jul 23, 2020
e87006d
In case of fail fast when deserializing invalid signature bytes, just…
Nashatyrev Jul 23, 2020
902ec56
Fix reference tests running on Windows
Nashatyrev Jul 23, 2020
47ca1c6
Merge remote-tracking branch 'pegasys/master' into feature-blst
Nashatyrev Jul 23, 2020
b204b52
Add Mikuli fallback when couldn't load Blst
Nashatyrev Jul 24, 2020
569e742
Apply spotless
Nashatyrev Jul 24, 2020
e683667
Move SWIG wrapper to a separate project
Nashatyrev Jul 24, 2020
41e647a
Fix errorprone warns
Nashatyrev Jul 24, 2020
e08b756
Update license for jblst
Nashatyrev Jul 24, 2020
65f0f50
Put the blst artifact version to versions.gradle
Nashatyrev Jul 24, 2020
acebcca
Implement BlstSecretKey.destroy()
Nashatyrev Jul 27, 2020
db88ac0
Temporary handling infinite pubkey/signature as a special case until …
Nashatyrev Jul 27, 2020
6047663
Apply spotless
Nashatyrev Jul 27, 2020
8318548
Resolve warnings
Nashatyrev Jul 27, 2020
1f9f688
Merge remote-tracking branch 'pegasys/master' into feature-blst
Nashatyrev Jul 27, 2020
aae5ea0
Generate KeyPair from seed in an implementation independent way. Thro…
Nashatyrev Jul 28, 2020
193c8f5
Fix tests
Nashatyrev Jul 28, 2020
90a35da
Apply spotless
Nashatyrev Jul 28, 2020
ce26e6a
Rename method to a more specific: aggregateSignatures()
Nashatyrev Jul 28, 2020
6c02825
Fix test compilation error
Nashatyrev Jul 28, 2020
8a2069c
Apply spotless
Nashatyrev Jul 28, 2020
d82db16
Make BLSPublicKey.getPublicKey() package private
Nashatyrev Jul 28, 2020
1de2060
Allow empty list signatures aggregation
Nashatyrev Jul 28, 2020
1659356
Disallow empty list signatures aggregation (revert prev commit)
Nashatyrev Jul 28, 2020
d0b27d4
Fix verify aggregated signature against multiple pubKey:message
Nashatyrev Jul 28, 2020
4978c2b
Fix blst equal pubkeys aggregation
Nashatyrev Jul 29, 2020
ef1fa1c
Make BLSSignature.getSignature() package private
Nashatyrev Jul 29, 2020
fa8ec98
Merge remote-tracking branch 'pegasys/master' into feature-blst
Nashatyrev Jul 29, 2020
f9577a0
Remove obsolete comment
Nashatyrev Jul 30, 2020
0c248d9
Moved BlstSignature specific methods from BlstBLS12381 to BlstSignatu…
Nashatyrev Jul 30, 2020
7447b6f
Fix batch verify case with invalid infinity signature
Nashatyrev Jul 30, 2020
fd219cc
Add lost test case
Nashatyrev Jul 30, 2020
654e2fa
Left comment for disabled test
Nashatyrev Jul 30, 2020
ea92796
Add explicit assertion to the test
Nashatyrev Jul 30, 2020
d903419
Apply spotless
Nashatyrev Jul 30, 2020
d17224f
Make BlstBLS12381.INSTANCE initialization more reliable and make it a…
Nashatyrev Jul 30, 2020
25fb802
Move BLS constants to BLSConstants class. Make a clear separation of …
Nashatyrev Jul 30, 2020
5dca043
Make a distinction of BLSSignature from/toSSZBytes() and from/toBytes…
Nashatyrev Jul 30, 2020
913e484
Add try/catch/finally to explicitly delete native structures
Nashatyrev Jul 30, 2020
7ade0ab
Minor BLS tests cleanups
Nashatyrev Jul 30, 2020
b60a046
Move BLS.aggregatePublicKeys() -> BLSPublicKey.aggregate(), rename BL…
Nashatyrev Jul 30, 2020
377eaf1
Merge branch 'master' into feature-blst
Nashatyrev Jul 30, 2020
d7559ca
Merge branch 'master' into feature-blst
Nashatyrev Jul 31, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*.bat -text
*.pcap binary
*.blocks binary
*.dll binary
*.dylib binary
*.so binary
*.gz binary
Expand Down
2 changes: 2 additions & 0 deletions bls/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ dependencies {
implementation 'org.apache.tuweni:tuweni-crypto'
implementation 'org.apache.tuweni:tuweni-ssz'
implementation 'org.miracl.milagro.amcl:milagro-crypto-java'
implementation 'tech.pegasys:jblst'


testImplementation project(':logging')
testImplementation('com.googlecode.json-simple:json-simple') {
Expand Down
15 changes: 13 additions & 2 deletions bls/src/main/java/tech/pegasys/teku/bls/BLS.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import tech.pegasys.teku.bls.impl.BLS12381;
import tech.pegasys.teku.bls.impl.PublicKey;
import tech.pegasys.teku.bls.impl.PublicKeyMessagePair;
import tech.pegasys.teku.bls.impl.blst.BlstBLS12381;
import tech.pegasys.teku.bls.impl.mikuli.MikuliBLS12381;

/**
Expand All @@ -43,7 +44,8 @@ public class BLS {

private static final Logger LOG = LogManager.getLogger();

private static BLS12381 BlsImpl = MikuliBLS12381.INSTANCE;
private static final BLS12381 BlsImpl =
BlstBLS12381.INSTANCE.map(bls -> (BLS12381) bls).orElse(MikuliBLS12381.INSTANCE);

/*
* The following are the methods used directly in the Ethereum 2.0 specifications. These strictly adhere to the standard.
Expand Down Expand Up @@ -80,6 +82,13 @@ public static boolean verify(BLSPublicKey publicKey, Bytes message, BLSSignature
return signature.getSignature().verify(publicKey.getPublicKey(), message);
}

public static BLSPublicKey aggregatePublicKeys(List<BLSPublicKey> publicKeys) {
return new BLSPublicKey(
getBlsImpl()
.aggregatePublicKeys(
publicKeys.stream().map(BLSPublicKey::getPublicKey).collect(Collectors.toList())));
}

Nashatyrev marked this conversation as resolved.
Show resolved Hide resolved
/**
* Aggregates a list of BLSSignatures into a single BLSSignature.
*
Expand All @@ -91,8 +100,10 @@ public static boolean verify(BLSPublicKey publicKey, Bytes message, BLSSignature
*
* @param signatures the list of signatures to be aggregated
* @return the aggregated signature
* @throws IllegalArgumentException if any of supplied signatures is invalid
*/
public static BLSSignature aggregate(List<BLSSignature> signatures) {
public static BLSSignature aggregateSignatures(List<BLSSignature> signatures)
Nashatyrev marked this conversation as resolved.
Show resolved Hide resolved
throws IllegalArgumentException {
checkArgument(signatures.size() > 0, "Aggregating zero signatures is invalid.");
return new BLSSignature(
getBlsImpl()
Expand Down
12 changes: 12 additions & 0 deletions bls/src/main/java/tech/pegasys/teku/bls/BLSConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,20 @@

package tech.pegasys.teku.bls;

import java.math.BigInteger;
import java.nio.ByteOrder;
import org.apache.tuweni.bytes.Bytes32;

public class BLSConstants {

public static final int BLS_PUBKEY_SIZE = 48;
public static final int BLS_SIGNATURE_SIZE = 96;

static final Bytes32 CURVE_ORDER_BYTES =
Bytes32.fromHexString("0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001");
static final BigInteger CURVE_ORDER_BI =
CURVE_ORDER_BYTES.toUnsignedBigInteger(ByteOrder.BIG_ENDIAN);

public static boolean VERIFICATION_DISABLED = false;

public static void disableBLSVerification() {
Expand Down
6 changes: 5 additions & 1 deletion bls/src/main/java/tech/pegasys/teku/bls/BLSKeyPair.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import com.google.common.base.MoreObjects;
import java.security.SecureRandom;
import java.util.Objects;
import java.util.Random;
import org.apache.tuweni.bytes.Bytes32;
import tech.pegasys.teku.bls.impl.KeyPair;

public final class BLSKeyPair {
Expand All @@ -39,7 +41,9 @@ public static BLSKeyPair random(final SecureRandom srng) {
* @return a keypair generated from a seed
*/
public static BLSKeyPair random(int seed) {
return new BLSKeyPair(BLS.getBlsImpl().generateKeyPair(seed));
BLSSecretKey pseudoRandomSecretBytes =
BLSSecretKey.fromBytesModR(Bytes32.random(new Random(seed)));
return new BLSKeyPair(pseudoRandomSecretBytes);
}

private final BLSPublicKey publicKey;
Expand Down
97 changes: 55 additions & 42 deletions bls/src/main/java/tech/pegasys/teku/bls/BLSPublicKey.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@

import static com.google.common.base.Preconditions.checkArgument;

import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import java.util.List;
import java.util.Objects;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes48;
import org.apache.tuweni.ssz.SSZ;
import tech.pegasys.teku.bls.impl.PublicKey;
import tech.pegasys.teku.ssz.sos.SimpleOffsetSerializable;
Expand All @@ -26,7 +29,7 @@ public final class BLSPublicKey implements SimpleOffsetSerializable {

// The number of SimpleSerialize basic types in this SSZ Container/POJO.
public static final int SSZ_FIELD_COUNT = 1;
public static final int BLS_PUBKEY_SIZE = 48;
public static final int SSZ_BLS_PUBKEY_SIZE = BLSConstants.BLS_PUBKEY_SIZE;

/**
* Generates a compressed, serialized, random, valid public key based on a seed.
Expand All @@ -43,7 +46,7 @@ public static BLSPublicKey random(int seed) {
* @return the empty public key as per the Eth2 spec
*/
public static BLSPublicKey empty() {
return BLSPublicKey.fromBytes(Bytes.wrap(new byte[BLS_PUBKEY_SIZE]));
return BLSPublicKey.fromBytesCompressed(Bytes48.ZERO);
}

@Override
Expand All @@ -53,36 +56,49 @@ public int getSSZFieldCount() {

@Override
public List<Bytes> get_fixed_parts() {
return List.of(toBytes());
return List.of(toSSZBytes());
}

public static BLSPublicKey fromBytes(Bytes bytes) {
public static BLSPublicKey fromSSZBytes(Bytes bytes) {
checkArgument(
bytes.size() == BLS_PUBKEY_SIZE,
"Expected " + BLS_PUBKEY_SIZE + " bytes but received %s.",
bytes.size() == SSZ_BLS_PUBKEY_SIZE,
"Expected " + SSZ_BLS_PUBKEY_SIZE + " bytes but received %s.",
bytes.size());
return SSZ.decode(
bytes,
reader ->
new BLSPublicKey(
BLS.getBlsImpl().publicKeyFromCompressed(reader.readFixedBytes(BLS_PUBKEY_SIZE))));
reader -> new BLSPublicKey(Bytes48.wrap(reader.readFixedBytes(SSZ_BLS_PUBKEY_SIZE))));
}

public static BLSPublicKey fromBytesCompressed(Bytes bytes) {
return new BLSPublicKey(BLS.getBlsImpl().publicKeyFromCompressed(bytes));
}

private final PublicKey publicKey;

/**
* Copy constructor.
* Create a PublicKey from 48-byte compressed format
*
* @param publicKey A BLSPublicKey
* @param bytes 48 bytes to read the public key from
* @return a public key. Note that implementation may lazily evaluate passed bytes so the method
* may not immediately fail if the supplied bytes are invalid. Use {@link
* BLSPublicKey#fromBytesCompressedValidate(Bytes48)} to validate immediately
* @throws IllegalArgumentException If the supplied bytes are not a valid public key However if
* implementing class lazily parses bytes the exception might not be thrown on invalid input
* but throw on later usage. Use {@link BLSPublicKey#fromBytesCompressedValidate(Bytes48)} if
* need to immediately ensure input validity
*/
public BLSPublicKey(BLSPublicKey publicKey) {
this.publicKey = publicKey.getPublicKey();
public static BLSPublicKey fromBytesCompressed(Bytes48 bytes) throws IllegalArgumentException {
return new BLSPublicKey(bytes);
}

public static BLSPublicKey fromBytesCompressedValidate(Bytes48 bytes)
throws IllegalArgumentException {
BLSPublicKey ret = new BLSPublicKey(bytes);
ret.getPublicKey().forceValidation();
return ret;
}

// Sometimes we are dealing with random, invalid pubkey points, e.g. when testing.
// Let's only interpret the raw data into a point when necessary to do so.
// And vice versa while aggregating we are dealing with points only so let's
// convert point to raw data when necessary to do so.
private final Supplier<PublicKey> publicKey;
private final Supplier<Bytes48> bytesCompressed;

/**
* Construct from a BLSSecretKey object.
*
Expand All @@ -97,42 +113,39 @@ public BLSPublicKey(BLSSecretKey secretKey) {
*
* @param publicKey A Mikuli PublicKey
*/
public BLSPublicKey(PublicKey publicKey) {
BLSPublicKey(PublicKey publicKey) {
this(() -> publicKey, Suppliers.memoize(publicKey::toBytesCompressed));
}

BLSPublicKey(Bytes48 bytesCompressed) {
this(
Suppliers.memoize(() -> BLS.getBlsImpl().publicKeyFromCompressed(bytesCompressed)),
() -> bytesCompressed);
}

private BLSPublicKey(Supplier<PublicKey> publicKey, Supplier<Bytes48> bytesCompressed) {
this.publicKey = publicKey;
this.bytesCompressed = bytesCompressed;
}

/**
* Returns the SSZ serialization of the <em>compressed</em> form of the signature.
*
* @return the serialization of the compressed form of the signature.
*/
public Bytes toBytes() {
public Bytes toSSZBytes() {
return SSZ.encode(
writer -> {
writer.writeFixedBytes(publicKey.toBytesCompressed());
writer.writeFixedBytes(toBytesCompressed());
});
}

public Bytes toBytesCompressed() {
return publicKey.toBytesCompressed();
}

public PublicKey getPublicKey() {
return publicKey;
public Bytes48 toBytesCompressed() {
return bytesCompressed.get();
}

/**
* Force validation of the given key's contents.
*
* @return true if the given key is valid, false otherwise
*/
public boolean isValid() {
try {
getPublicKey().forceValidation();
return true;
} catch (IllegalArgumentException e) {
return false;
}
PublicKey getPublicKey() {
return publicKey.get();
}

@Override
Expand All @@ -155,11 +168,11 @@ public boolean equals(Object obj) {
}

BLSPublicKey other = (BLSPublicKey) obj;
return Objects.equals(this.getPublicKey(), other.getPublicKey());
return Objects.equals(this.toBytesCompressed(), other.toBytesCompressed());
Nashatyrev marked this conversation as resolved.
Show resolved Hide resolved
}

@Override
public int hashCode() {
return Objects.hash(publicKey);
return Objects.hash(toBytesCompressed());
}
}
55 changes: 43 additions & 12 deletions bls/src/main/java/tech/pegasys/teku/bls/BLSSecretKey.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,44 @@

package tech.pegasys.teku.bls;

import java.math.BigInteger;
import java.nio.ByteOrder;
import java.util.Objects;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import tech.pegasys.teku.bls.impl.SecretKey;

public final class BLSSecretKey {
/**
* Creates a secret key instance from bytes
*
* @param bytes Should be in range [0, )
* @throws IllegalArgumentException if bytes are not in the valid range
*/
public static BLSSecretKey fromBytes(Bytes32 bytes) throws IllegalArgumentException {
if (bytes.compareTo(BLSConstants.CURVE_ORDER_BYTES) >= 0) {
throw new IllegalArgumentException(
"Invalid bytes for secret key (0 <= SK < r, where r is "
+ BLSConstants.CURVE_ORDER_BYTES
+ "): "
+ bytes);
} else {
return new BLSSecretKey(BLS.getBlsImpl().secretKeyFromBytes(bytes));
}
}

public static BLSSecretKey fromBytes(Bytes32 bytes) {
return new BLSSecretKey(BLS.getBlsImpl().secretKeyFromBytes(bytes));
static BLSSecretKey fromBytesModR(Bytes32 secretKeyBytes) {
final Bytes32 keyBytes;
if (secretKeyBytes.compareTo(BLSConstants.CURVE_ORDER_BYTES) >= 0) {
BigInteger validSK =
secretKeyBytes
.toUnsignedBigInteger(ByteOrder.BIG_ENDIAN)
.mod(BLSConstants.CURVE_ORDER_BI);
keyBytes = Bytes32.leftPad(Bytes.wrap(validSK.toByteArray()));
} else {
keyBytes = secretKeyBytes;
}
return fromBytes(keyBytes);
}

private SecretKey secretKey;
Expand All @@ -35,19 +64,16 @@ public BLSSecretKey(SecretKey secretKey) {
this.secretKey = secretKey;
}

public SecretKey getSecretKey() {
SecretKey getSecretKey() {
return secretKey;
}

public Bytes toBytes() {
final Bytes bytes = secretKey.toBytes();
if (bytes.size() == 48) {
final int paddingLength = 48 - 32;
if (bytes.slice(0, paddingLength).isZero()) {
return bytes.slice(paddingLength, 32);
}
}
return bytes;
public BLSPublicKey toPublicKey() {
return new BLSPublicKey(getSecretKey().derivePublicKey());
}

public Bytes32 toBytes() {
return secretKey.toBytes();
}

/** Overwrites the key with zeros so that it is no longer in memory */
Expand All @@ -67,4 +93,9 @@ public boolean equals(final Object o) {
public int hashCode() {
return Objects.hash(secretKey);
}

@Override
public String toString() {
return "BLSSecretKey{" + toBytes() + '}';
}
}