IOTA:Signature And Validation
link here。
First of all, create Private Key from Seed.
// length = security (1: light client, 2: wallet default, 3: exchange level)
var key = function(seed, index, length) {
...
return key; // private key
}
- have your Private Key ready.
- divides the Private Key into 'L' segments, where
L = security * 27
.- Hash all segments as a whole. The product is called
digest
.- Hash
digest
twice. The product is calledaddress
.
Signature is used to sign anything(=signed data usually bundle) on tangle that belongs to you with your private key.
- have your Private Key ready.
- divides the Private Key into 'L' segments, where
L = security * 27
.- For each i-th segment, hash N_i times, where N_i is caluculated as followed:
How to get N
For each i-th tryte of the Signed Data, get decimal 'd' of the tryte. Converter here. e.g) tryte[9] corresponds to
d=0
, [A] is tod=1
...[M] is tod=13
, L is tod=-13
...Y is tod=-2
, Z is tod=-1
).Formula: N_i = 13 - d
- Those hashed segments are called Signature together.
- have your Signature ready.
- divides the Signature into 'L' segments, where
L = security * 27
.- For each i-th segment, hash M_i times, where M_i is caluculated as followed:
How to get M (basically main idea is same as N.)
For each i-th tryte of the Signed Data, get decimal 'd' of the tryte. Converter here. e.g) tryte[9] corresponds to
d=0
, [A] is tod=1
...[M] is tod=13
, L is tod=-13
...Y is tod=-2
, Z is tod=-1
).Formula: M_i = 13 + d
- Hash those segment together and get
digest
.- Hash the
digest
twice.- Check if the product of step 5 matches the address of the signed data(usually Bundle).
Signature is used to sign your input address you spend. And signature is stored in the bundle that spends the signed input. Signature data (length = security * 2187 tryte) is stored in signatureFragment
. (Note that signatureFragment
's capacity is 2187 tryte, so the larger security, the more transactions for storing signature are necessary to be included in the bundle.)
Signed data mentioned above refers to the bundle hash (81 tryte) that includes the signature.(Strictly speaking, signed data is called normalized bundle hash
, which is slightly incremented bundle hash such that total exposure of the private key would be half.)
data[0], data[1], data[2], which are components of normailized bundle hash, are used as Signed Data. How many times to hash each of 27 segments coressponds to each tryte of 27 trytes signed data. data[i] above is 27 trytes of signed data. if security = 1
, data[0] is used. if security = 2
, data[0] and data[1] is used such that in total, 54 trytes are used to sign. (table above)
Recall the when creating bundle, numbers of transactions that store signature depends also on the security level. That was because as security level increases, more data[i] is used to sign.
Even though API raises error, security >= 4
is allowed in protocol (i.e. transaction is confirmed), in this case, data[3] does not exist so use data[0] again and so on.
/**
* Normalized the bundle.
* return the bundle each tryte is written in integer[-13~13]
*
* @param bundleHash BundleのHash。
* @return normalizedBundle A normalized bundle hash.
*/
public int[] normalizedBundle(String bundleHash) {
// normalized Bundle 81 trytes.
int[] normalizedBundle = new int[81];
// divides bundle hash into three sections, 27 trytes each.
for (int i = 0; i < 3; i++) {
long sum = 0;
// check each tryte in a section.
// get corresponding integer [-13~13]. And add it to sum.
for (int j = 0; j < 27; j++) {
// sum += value, where
// value = integer value of i*27+j-th tryte
sum +=
(normalizedBundle[i * 27 + j] =
// Convert tryte[9ABC...Z] into [-13~13]
Converter.value(Converter.tritsString("" + bundleHash.charAt(i * 27 + j)))
);
}
// if sum of the section >= 0
if (sum >= 0) {
// until sum = 0
while (sum-- > 0) {
// decrement tryte
for (int j = 0; j < 27; j++) {
if (normalizedBundle[i * 27 + j] > -13) {
normalizedBundle[i * 27 + j]--;
break;
}
}
}
// if sum of the section < 0
} else {
// until sum = 0
while (sum++ < 0) {
// increment tryte
for (int j = 0; j < 27; j++) {
if (normalizedBundle[i * 27 + j] < 13) {
normalizedBundle[i * 27 + j]++;
break;
}
}
}
}
}
return normalizedBundle;
}
You must have seen the warnings "Do not reuse the address!". But, what's that? And why can't we just simply use same address? Secret is here. Recall:
How to get N
For each i-th tryte of the Signed Data, get decimal 'd' of the tryte. Converter here. e.g) tryte[9] corresponds to
d=0
, [A] is tod=1
...[M] is tod=13
, L is tod=-13
...Y is tod=-2
, Z is tod=-1
).Formula: N_i = 13 - d
Number of hashings depends on i-th tryte of signed data. If signed data contains a lot of 'M', which requires zero hashing. This may result in the exposure of the part of your raw private key.
This kind of signing mechanism originates from Winternitz one-time signature.
IOTA iotaledger https://github.com/iotaledger
signing part: JavaScript=>signing.js.
Java=>Signing.java.
@abmushi on twitter, discord
Translated from my original article written in Japanese here
Donation is always welcome and appreciated!
BTC: 1ACGpgpAMgaAKbGpPq2sDa467MnRNdW4wX
IOTA: KWIEEQHAJBJTDPE9WEDILKMVQCJPZSF9CXALYQTULCGNPLIIKJLFYHCWSJNXDALKHAOOTELQUIXWIOFVDPQNXMLBZB