(image)

Programming Hotmoka
A tutorial on Hotmoka and smart contracts in Takamaka

8.8 Signatures and quantum-resistance

Hotmoka is agnostic wrt. the algorithm used for signing requests. This means that it is possible to deploy Hotmoka nodes that sign requests with distinct signature algorithms. Of course, if nodes must re-execute the same transactions, such as in the case of a blockchain, then all nodes of the blockchain must use the same algorithm for the transactions signed by each given account, or otherwise they will not be able to reach consensus. Yet, any fixed algorithm can be chosen for each account. In principle, it is even possible to use an algorithm that does not sign the transactions, if the identity of the callers of the transactions needn’t be verified. However, this might be sensible in private networks only.

The default signature algorithm used by a node is specified at construction time, as a configuration parameter. For instance, the code

var config = TendermintNodeConfigBuilders.defaults().build();
var consensus = TendermintConsensusConfigBuilders.defaults()
    .setPublicKeyOfGamete(keys.getPublic())
    .setInitialSupply(SUPPLY)
    ...
    .setSignatureForRequests(SignatureAlgorithms.ed25519()) // this is the default
    .build();


try (var node = TendermintNodes.init(config);
          var initialized = TendermintInitializedNodes.of(node, consensus, takamakaCodePath)) {
    ...
}

starts a Tendermint-based blockchain node that uses the ed25519 signature algorithm as default signature algorithm for the requests. Requests sent to that node can be signed as follows:

// recover the algorithm used by the node
SignatureAlgorithm signature = node.getConfig().getSignatureForRequests();


// create a key pair for that algorithm
KeyPair keys = signature.getKeyPair();


// create a signer object with the private key of the key pair
Signer<SignedTransactionRequest<?>> signer = signature.getSigner
  (keys.getPrivate(), SignedTransactionRequest<?>::toByteArrayWithoutSignature);


// create an account having public key keys.getPublic()
var account = ....


// create a transaction request on behalf of the account
ConstructorCallTransactionRequest request
  = TransactionRequests.constructorCall(signer, account, ...);


// send the request to the node
node.addConstructorCallTransaction(request);

In the example above, we have explicitly specified to use ed25519 as default signature algorithm. That is what is chosen if nothing is specified at configuration-time. Consequently, there is no need to specify that algorithm in the configuration object and that is why we never did it in the previous chapters. But it is possible to configure nodes with other default signature algorithms. For instance:

var consensus = TendermintConsensusConfigBuilders.defaults()
  .setPublicKeyOfGamete(keys.getPublic())
  .setInitialSupply(SUPPLY)
  ...
  .setSignatureForRequests(SignatureAlgorithms.sha256dsa()) // this replaces the default
  .build();

configures a node that uses sha256dsa as default signature algorithm, while

var consensus = TendermintConsensusConfigBuilders.defaults()
  .setPublicKeyOfGamete(keys.getPublic())
  .setInitialSupply(SUPPLY)
  ...
  .setSignatureForRequests(SignatureAlgorithms.empty())
  .build();

configures a node that uses the empty signature as default signature algorithm; it is an algorithm that accepts all signatures, in practice disabling signature checking.

It is possible to specify a quantum-resistant signature algorithm as default, that is, one that belongs to a family of algorithms that are expected to be immune from attacks performed through a quantistic computer. For instance,

var consensus = TendermintConsensusConfigBuilders.defaults()
  .setPublicKeyOfGamete(keys.getPublic())
  .setInitialSupply(SUPPLY)
  ...
  .setSignatureForRequests(SignatureAlgorithms.qtesla1())
  .build();

configures a node that uses the quantum-resistant qtesla-p-I algorithm as default signature algorithm, while

var consensus = TendermintConsensusConfigBuilders.defaults()
  .setPublicKeyOfGamete(keys.getPublic())
  .setInitialSupply(SUPPLY)
  ...
  .setSignatureForRequests(SignatureAlgorithms.qtesla3())
  .build();

configures a node that uses the quantum-resistant qtesla-p-III algorithm as default signature algorithm, that is expected to be more resistant than qtesla-p-I but has larger signatures than qtesla-p-I.

Quantum-resistance is an important aspect of future-generation blockchains. However, at the time of this writing, a quantum attack is mainly a theoretical possibility, while the large size of quantum-resistant keys and signatures is already a reality and a node using a qtesla signature algorithm as default might exhaust the disk space of your computer very quickly. In practice, it is better to use a quantum-resistant signature algorithm only for a subset of the transactions, whose quantum-resistance is deemed important. Instead, one should use a lighter algorithm (such as the default ed25519) for all other transactions. This is possible because Hotmoka nodes allow one to mix transactions signed with distinct algorithms. Namely, one can use ed25519 as default algorithm, for all transactions signed by instances of ExternallyOwnedAccounts, with the exception of those transactions that are signed by instances of the interface AccountQTESLA1, such as the class ExternallyOwnedAccountQTESLA1, or of the interface AccountQTESLA3, such as the class ExternallyOwnedAccountQTESLA3, or of the interface AccountSHA256DSA, such as the class ExternallyOwnedAccountSHA256DSA (see Fig. 4.1). Namely, if the caller of a transaction is an AccountQTESLA1, then the request of the transaction must be signed with the qtesla-p-I algorithm. If the caller of a transaction is an AccountQTESLA3, then the request of the transaction must be signed with the qtesla-p-III algorithm. If the caller of a transaction is an AccountSHA256DSA, then the request of the transaction must be signed with the sha256dsa algorithm. If the caller of a transaction is an AccountED25519, then the request of the transaction must be signed with the ed25519 algorithm. In practice, this allows specific transactions to override the default signature algorithm for the node.

Let us for instance create an account that uses the default signature algorithm for the node. We charge its creation to the faucet of the node:

moka keys create --name=account7.pem --password
Enter value for --password (the password that will be needed later to use the key pair): orange
The new key pair has been written into "account7.pem":
* public key: ANmTaQFXiD1WnhaLLpnd3T6YHJrJDVuXfkJ99HYo79YJ (ed25519, base58)
* public key: i0yl/D3QnFdUY/0AD4myYMMAG+U0iLVR1CChL4Immmc= (ed25519, base64)
* Tendermint-like address: 14F01EB311232E884AAAFD7D4F0762B9CB9B73C1
moka accounts create faucet 1000000000000 account7.pem --password --yes --uri ws://panarea.hotmoka.io:8001
Enter value for --password (the password of the key pair): orange
A new account a5522145eab808a984ba659fb9c52778bf11567119359eafb3af7a5e99901038#0 has been created.
Its key pair has been saved into the file "a5522145eab808a984ba659fb9c52778bf11567119359eafb3af7a5e99901038#0.pem".


Gas consumption:
 * total: 60076
   * for CPU: 15846
   * for RAM: 7270
   * for storage: 36960
   * for penalty: 0
 * price per unit: 1 pana
 * total price: 60076 panas

You can check the class of the new account with the moka objects show command:

moka objects show a5522145eab808a984ba659fb9c52778bf11567119359eafb3af7a5e99901038#0 --uri ws://panarea.hotmoka.io:8001
class io.takamaka.code.lang.ExternallyOwnedAccountED25519 (from jar installed at 68e90e2868751a3f22fd5fad3ceef377b91b3e52fdd30705f57505e22902ce40)
  io.takamaka.code.lang.Contract.balance:java.math.BigInteger = 1000000000000
  io.takamaka.code.lang.ExternallyOwnedAccount.nonce:java.math.BigInteger = 0
  io.takamaka.code.lang.ExternallyOwnedAccount.publicKey:java.lang.String = "i0yl/D3QnFdUY/0AD4myYMMAG+U0iLVR1CChL4Immmc="

As you can see, an account has been created, that uses the default ed25519 signature algorithm of the node. Assume that we want to create an account now, that always uses the sha256dsa signature algorithm instead, regardless of the default signature algorithm of the node. We can specify that when invoking the moka accounts create command:

moka keys create --name=account8.pem --password --signature=sha256dsa
Enter value for --password (the password that will be needed later to use the key pair): play
The new key pair has been written into "account8.pem":
* public key: 7sNSqvEbG3PWHnn8JYcB4wBJyGKHTbE9tRyTy84yfSYQnXJZoceNy8YFHUhjmsNS9wEV3FHhAncMk45eS4rcFSJgJ3AxKyf5xewfFxygnpSh61rPNbcVZDTfzBxmy5o4vyU9oXEBJgUKAhyLMhhiWspG4Tbxw5Ya4DFigJ4czDKGJ4JhHzXwV8a9u8SgMeNwj5wW57RP... (sha256dsa, base58)
* public key: MIIDRjCCAjkGByqGSM44BAEwggIsAoIBAQCVR1z12T5ZbD/NHZAq3QL0J/XzxyEDE7tF+01bsuX+HL1njNS73YTJg2vh8xwHd3Ja62wvw4uF9IB2+na82BRsyJpvsvcG3XGYmMIIPcjYlvhAYuLJyU0TewVKjYCWrbjVGVI5juyoUqCvEt+D5HWqZdTsDDipVg1WYRhv... (sha256dsa, base64)
* Tendermint-like address: F37FA187C101541C5721EA4996EB1F701904220F
moka accounts create faucet 1000000000000 account8.pem --password --signature=sha256dsa --yes --uri ws://panarea.hotmoka.io:8001
Enter value for --password (the password of the key pair): play
A new account 9b7158bc38140c3fa36f3ddb6a1919c5c81d295d9624727c8fade9225bc24500#0 has been created.
Its key pair has been saved into the file "9b7158bc38140c3fa36f3ddb6a1919c5c81d295d9624727c8fade9225bc24500#0.pem".


Gas consumption:
 * total: 232796
   * for CPU: 15846
   * for RAM: 7270
   * for storage: 209680
   * for penalty: 0
 * price per unit: 1 pana
 * total price: 232796 panas

This creation has been more expensive than for the previous account, because the public key of the sha256dsa algorithm is much longer than that for the ed25519 algorithm. You can verify this with the moka objects show command:

moka objects show 9b7158bc38140c3fa36f3ddb6a1919c5c81d295d9624727c8fade9225bc24500#0 --uri ws://panarea.hotmoka.io:8001
class io.takamaka.code.lang.ExternallyOwnedAccountSHA256DSA (from jar installed at 68e90e2868751a3f22fd5fad3ceef377b91b3e52fdd30705f57505e22902ce40)
  io.takamaka.code.lang.Contract.balance:java.math.BigInteger = 1000000000000
  io.takamaka.code.lang.ExternallyOwnedAccount.nonce:java.math.BigInteger = 0
  io.takamaka.code.lang.ExternallyOwnedAccount.publicKey:java.lang.String = "MIIDRjCCAjkGByqGSM44BAEwggIsAoIBAQCVR1z12T5ZbD/NHZAq3QL0J/XzxyEDE7tF+01bsuX+HL1njNS73YTJg2vh8xwHd3Ja62wvw4uF9IB2+
na82BRsyJpvsvcG3XGYmMIIPcjYlvhAYuLJyU0TewVKjYCWrbjVGVI5juyoUqCvEt+D5HWqZdTsDDipVg1WYRhv+Yufyetg7uiwMDdrI2vHO+Os29dP1hwdJHX6MHe48IBGeIH/fhylb+4GbXlQat5R7btUQ6Vjkn28S6UgCGdGF1yIhZJevGTGFHkGdzSWmQy3FOxmcwTiYfruM7PL3wCODD+pBlDZfTkJySdb9KyG/8
s9A+bfyK2lk0JC3W07zKKkBssLAiEA+Bg2aLpfxbsGtZgebYt5XTC4l41Dyg7FcuN+CZOal3MCggEAQt67naWz2IzJVuCHh+w/Ogm7pfSLiJp0qvUxdKoPvn48W4/NelO+9WOw6YVgMolgqVF/QBTTMl/
Hlivx4Ek3DXbRMUp2E355Lz8NuFnQleSluTICTweezy7wnHl0UrB3DhNQeC7Vfd95SXnc7yPLlvGDBhllxOvJPJxxxWuSWVWnX5TMzxRJrEPVhtC+7kMlGwsihzSdaN4NFEQD8T6AL0FG2ILgV68ZtvYnXGZ2yPoOPKJxOjJX/Rsn0GOfaV40fY0c+ayBmibKmwTLDrm3sDWYjRW7rGUhKlUjnPx+WPrjjXJQq5mR/7
yXE0Al/ozgTEOZrZZWm+kaVG9JeGk8egOCAQUAAoIBABsKwoLbwxijsuaLQzeICPOQOGdrW0iFCFIEv7SPTrN2g/GTfDEaE/QuKwOvheisEAssER6uGVfsCkDZiA4zjBKAghzLShKv1eSFDe2G3nijI0FlXCkDGpAbddZUI9yyge1di3mHvmqdvkUMovRPOGbkVlg3gBfxqgRcByuaKq7frVJtI/23
HkQLnGCBdYiDQP9ZRjlGloEkLbGJBW1bGj7aC+KUeChUlIB0YvvwTlBKDB/Cl7YQ9JfdpdOF/O7icpVfxNS0wjPZmBWSVC6Ir9scTPYdbhiI2wPpDfi9A2UDYBHmEbhADJUA3kR1E6RSBzlihf6FSD2aY68jxt3b4ts="

Note that the class of the account is ExternallyOwnedAccountSHA256DSA this time.

Let us create an account that uses the qtesla-p-I signature algorithm now:

moka keys create --name=account9.pem --password --signature=quantum1
Enter value for --password (the password that will be needed later to use the key pair): quantum1
The new key pair has been written into "account9.pem":
* public key: 5Ku6q1BJpPtWJW9b2nnez9cxNWGTygAoRy6pDKFu1wYS4yqwme5rCxPR2a2B3ksimtZAuhGzn3W2ALCFjXfx2sNpQjd9red2c2w71KQBQeLm8fdcRsejypjjGcnRpFbjzGLN8Kn2ngqQsP5PCBUYqZ8e1gHBZ2RU4DuDBnyjRKCDczahvAME3WdnWCeuPreuqTsfE4iT... (qtesla1, base58)
* public key: MII6NDANBgsrBgEEAYGwGgIECwOCOiEAhW826TB57ThIOz54TGGyRf4ygae1wKPl76rqkC7ennvmI4DVfcS8qvqq5oicchmoPvkDingA+eJ1dp5BJBK/vldywg3MT6V5t6ANypYQAwYt/XbsR1hGu3S0SrGX/O1nIk+Emf6XtmE50pV1ES7fNvHxV1do/KBRovsA5um5... (qtesla1, base64)
* Tendermint-like address: 75609B79F6CD81349853EF6D70AB949E92860825
moka accounts create faucet 1000000000000 account9.pem --password --signature=qtesla1 --yes --uri ws://panarea.hotmoka.io:8001
Enter value for --password (the password of the key pair): quantum1
A new account 1ed8d6903f5fe0a97a3f588f3b500a49abc6627ea8d0046510259948b4752199#0 has been created.
Its key pair has been saved into the file "1ed8d6903f5fe0a97a3f588f3b500a49abc6627ea8d0046510259948b4752199#0.pem".


Gas consumption:
 * total: 3231996
   * for CPU: 15846
   * for RAM: 7270
   * for storage: 3208880
   * for penalty: 0
 * price per unit: 1 pana
 * total price: 3231996 panas

The creation of this account has been still more expensive, since this kind of quantum-resistant keys are very large. Again, you can use the moka object show command to verify that it has class ExternallyOwnedAccountQTESLA1.

Finally, let us use the previous qtesla-p-I account to create a qtesla-p-III account:

moka keys create --name=account10.pem --password --signature=quantum3
Enter value for --password (the password that will be needed later to use the key pair): quantum3
The new key pair has been written into "account10.pem":
* public key: 52z96jjVXCf3D9XkWHwT2kenXqU77HJXT4aykgDL1jhhyRZmDzg71nBKGKXEHDg8abTrjfUmayvXZhcSxXtzscE9Aim2bToexxEH1oKvNVgHMXWSyFAi7ZJUz6smqwosAPT8MtN27ysc2TDfMMveJWqV8xfX1K85uGVZ3gqUsZLf9wTsHSbmZ2DGExDTA1gp1kZAycM6... (qtesla3, base58)
* public key: MIKWNDANBgsrBgEEAYGwGgIEDAOCliEA+/6sb2kMAVDQBb3B2OSxUzt5xt9TFuTry3B2qVtegi1JncC2EsgP+t10H3IkYnOK1rqKcHQZ7lVa9EETqoYNsiaqgwXLqwCDpR5rSH8vlKXRlUsC8oOaBd4c1gVeBwtA9oQGMB55rWlmUXF3HS3prEadYmR2rOSTpGGEX86c... (qtesla3, base64)
* Tendermint-like address: A977A61DBB9999EC7A1F68BB2BE84730A1CAB20C
moka accounts create 1ed8d6903f5fe0a97a3f588f3b500a49abc6627ea8d0046510259948b4752199#0 100000 account10.pem --password --password-of-payer --signature=qtesla3 --yes --uri ws://panarea.hotmoka.io:8001
Enter value for --password (the password of the key pair): quantum3
Enter value for --password-of-payer (the password of the payer): quantum1
A new account 0840a5a5963aa2c5b3e4150c8c74d91ed56dcd2ff79636e970c1d3f5e47f6ce1#0 has been created.
Its key pair has been saved into the file "0840a5a5963aa2c5b3e4150c8c74d91ed56dcd2ff79636e970c1d3f5e47f6ce1#0.pem".


Gas consumption:
 * total: 8458589
   * for CPU: 15403
   * for RAM: 6546
   * for storage: 8436640
   * for penalty: 0
 * price per unit: 1 pana
 * total price: 8458589 panas

Note, again, the extremely high gas cost of this creation.

Regardless of the kind of account, their use is always the same. The only difference is to use the right signature algorithm when signing a transaction, since it must match that of the caller account. This is automatic, if we use the moka tool. For instance, let us use our qtesla-p-I account to install the io-hotmoka-tutorial-examples-family-1.11.5.jar file from Sec. 3.4 in the node:

moka jars install 1ed8d6903f5fe0a97a3f588f3b500a49abc6627ea8d0046510259948b4752199#0 io-hotmoka-tutorial-examples-family/target/io-hotmoka-tutorial-examples-family-1.11.5.jar --yes --password-of-payer --uri=ws://panarea.hotmoka.io:8001
Enter value for --password-of-payer (the password of the key pair of the payer account): quantum1
The jar has been installed at 97d5dce010b802a49d49946ac3be92ef241f34b17f7aa48cc2363c7c154e1e74.


Gas consumption:
 * total: 699992
   * for CPU: 15369
   * for RAM: 5183
   * for storage: 679440
   * for penalty: 0
 * price per unit: 7 panas
 * total price: 4899944 panas

The moka tool has understood that the payer is an account that signs with the qtesla-p-I algorithm and has signed the request accordingly.