Skip to content

Commit f47ff7a

Browse files
committed
basic SLH-DSA package
1 parent 411b417 commit f47ff7a

21 files changed

+2240
-0
lines changed
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package org.bouncycastle.pqc.crypto.slhdsa;
2+
3+
import org.bouncycastle.util.Arrays;
4+
import org.bouncycastle.util.Pack;
5+
6+
class ADRS
7+
{
8+
public static final int WOTS_HASH = 0;
9+
public static final int WOTS_PK = 1;
10+
public static final int TREE = 2;
11+
public static final int FORS_TREE = 3;
12+
public static final int FORS_PK = 4;
13+
public static final int WOTS_PRF = 5;
14+
public static final int FORS_PRF = 6;
15+
16+
static final int OFFSET_LAYER = 0;
17+
static final int OFFSET_TREE = 4;
18+
static final int OFFSET_TREE_HGT = 24;
19+
static final int OFFSET_TREE_INDEX = 28;
20+
static final int OFFSET_TYPE = 16;
21+
static final int OFFSET_KP_ADDR = 20;
22+
static final int OFFSET_CHAIN_ADDR = 24;
23+
static final int OFFSET_HASH_ADDR = 28;
24+
25+
final byte[] value = new byte[32];
26+
27+
ADRS()
28+
{
29+
}
30+
31+
ADRS(ADRS adrs)
32+
{
33+
System.arraycopy(adrs.value, 0, this.value, 0, adrs.value.length);
34+
}
35+
36+
public void setLayerAddress(int layer)
37+
{
38+
Pack.intToBigEndian(layer, value, OFFSET_LAYER);
39+
}
40+
41+
public int getLayerAddress()
42+
{
43+
return Pack.bigEndianToInt(value, OFFSET_LAYER);
44+
}
45+
46+
public void setTreeAddress(long tree)
47+
{
48+
// tree address is 12 bytes
49+
Pack.longToBigEndian(tree, value, OFFSET_TREE + 4);
50+
}
51+
52+
public long getTreeAddress()
53+
{
54+
return Pack.bigEndianToLong(value, OFFSET_TREE + 4);
55+
}
56+
57+
public void setTreeHeight(int height)
58+
{
59+
Pack.intToBigEndian(height, value, OFFSET_TREE_HGT);
60+
}
61+
62+
public int getTreeHeight()
63+
{
64+
return Pack.bigEndianToInt(value, OFFSET_TREE_HGT);
65+
}
66+
67+
public void setTreeIndex(int index)
68+
{
69+
Pack.intToBigEndian(index, value, OFFSET_TREE_INDEX);
70+
}
71+
72+
public int getTreeIndex()
73+
{
74+
return Pack.bigEndianToInt(value, OFFSET_TREE_INDEX);
75+
}
76+
77+
// resets part of value to zero in line with 2.7.3
78+
public void setType(int type)
79+
{
80+
Pack.intToBigEndian(type, value, OFFSET_TYPE);
81+
82+
Arrays.fill(value, 20, value.length, (byte)0);
83+
}
84+
85+
public void changeType(int type)
86+
{
87+
Pack.intToBigEndian(type, value, OFFSET_TYPE);
88+
}
89+
90+
public int getType()
91+
{
92+
return Pack.bigEndianToInt(value, OFFSET_TYPE);
93+
}
94+
95+
public void setKeyPairAddress(int keyPairAddr)
96+
{
97+
Pack.intToBigEndian(keyPairAddr, value, OFFSET_KP_ADDR);
98+
}
99+
100+
public int getKeyPairAddress()
101+
{
102+
return Pack.bigEndianToInt(value, OFFSET_KP_ADDR);
103+
}
104+
105+
public void setHashAddress(int hashAddr)
106+
{
107+
Pack.intToBigEndian(hashAddr, value, OFFSET_HASH_ADDR);
108+
}
109+
110+
public void setChainAddress(int chainAddr)
111+
{
112+
Pack.intToBigEndian(chainAddr, value, OFFSET_CHAIN_ADDR);
113+
}
114+
}
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
package org.bouncycastle.pqc.crypto.slhdsa;
2+
3+
import java.util.LinkedList;
4+
5+
import org.bouncycastle.util.Arrays;
6+
7+
class Fors
8+
{
9+
SLHDSAEngine engine;
10+
11+
public Fors(SLHDSAEngine engine)
12+
{
13+
this.engine = engine;
14+
}
15+
16+
// Input: Secret seed SK.seed, start index s, target node height z, public seed PK.seed, address ADRS
17+
// Output: n-byte root node - top node on Stack
18+
byte[] treehash(byte[] skSeed, int s, int z, byte[] pkSeed, ADRS adrsParam)
19+
{
20+
LinkedList<NodeEntry> stack = new LinkedList<NodeEntry>();
21+
22+
if (s % (1 << z) != 0)
23+
{
24+
return null;
25+
}
26+
27+
ADRS adrs = new ADRS(adrsParam);
28+
29+
for (int idx = 0; idx < (1 << z); idx++)
30+
{
31+
adrs.setType(ADRS.FORS_PRF);
32+
adrs.setKeyPairAddress(adrsParam.getKeyPairAddress());
33+
adrs.setTreeHeight(0);
34+
adrs.setTreeIndex(s + idx);
35+
36+
byte[] sk = engine.PRF(pkSeed, skSeed, adrs);
37+
38+
adrs.changeType(ADRS.FORS_TREE);
39+
40+
byte[] node = engine.F(pkSeed, adrs, sk);
41+
42+
adrs.setTreeHeight(1);
43+
44+
// while ( Top node on Stack has same height as node )
45+
while (!stack.isEmpty()
46+
&& ((NodeEntry)stack.get(0)).nodeHeight == adrs.getTreeHeight())
47+
{
48+
adrs.setTreeIndex((adrs.getTreeIndex() - 1) / 2);
49+
NodeEntry current = ((NodeEntry)stack.remove(0));
50+
51+
node = engine.H(pkSeed, adrs, current.nodeValue, node);
52+
//topmost node is now one layer higher
53+
adrs.setTreeHeight(adrs.getTreeHeight() + 1);
54+
}
55+
56+
stack.add(0, new NodeEntry(node, adrs.getTreeHeight()));
57+
}
58+
59+
return ((NodeEntry)stack.get(0)).nodeValue;
60+
}
61+
62+
public SIG_FORS[] sign(byte[] md, byte[] skSeed, byte[] pkSeed, ADRS paramAdrs)
63+
{
64+
ADRS adrs = new ADRS(paramAdrs);
65+
66+
int[] idxs = message_to_idxs(md, engine.K, engine.A);
67+
SIG_FORS[] sig_fors = new SIG_FORS[engine.K];
68+
// compute signature elements
69+
int t = engine.T;
70+
for (int i = 0; i < engine.K; i++)
71+
{
72+
// get next index
73+
int idx = idxs[i];
74+
// pick private key element
75+
adrs.setType(ADRS.FORS_PRF);
76+
adrs.setKeyPairAddress(paramAdrs.getKeyPairAddress());
77+
adrs.setTreeHeight(0);
78+
adrs.setTreeIndex(i * t + idx);
79+
80+
byte[] sk = engine.PRF(pkSeed, skSeed, adrs);
81+
82+
adrs.changeType(ADRS.FORS_TREE);
83+
84+
byte[][] authPath = new byte[engine.A][];
85+
// compute auth path
86+
for (int j = 0; j < engine.A; j++)
87+
{
88+
int s = (idx / (1 << j)) ^ 1;
89+
authPath[j] = treehash(skSeed, i * t + s * (1 << j), j, pkSeed, adrs);
90+
}
91+
sig_fors[i] = new SIG_FORS(sk, authPath);
92+
}
93+
return sig_fors;
94+
}
95+
96+
public byte[] pkFromSig(SIG_FORS[] sig_fors, byte[] message, byte[] pkSeed, ADRS adrs)
97+
{
98+
byte[][] node = new byte[2][];
99+
byte[][] root = new byte[engine.K][];
100+
int t = engine.T;
101+
102+
int[] idxs = message_to_idxs(message, engine.K, engine.A);
103+
// compute roots
104+
for (int i = 0; i < engine.K; i++)
105+
{
106+
// get next index
107+
int idx = idxs[i];
108+
// compute leaf
109+
byte[] sk = sig_fors[i].getSK();
110+
adrs.setTreeHeight(0);
111+
adrs.setTreeIndex(i * t + idx);
112+
node[0] = engine.F(pkSeed, adrs, sk);
113+
// compute root from leaf and AUTH
114+
byte[][] authPath = sig_fors[i].getAuthPath();
115+
116+
adrs.setTreeIndex(i * t + idx);
117+
for (int j = 0; j < engine.A; j++)
118+
{
119+
adrs.setTreeHeight(j + 1);
120+
if (((idx / (1 << j)) % 2) == 0)
121+
{
122+
adrs.setTreeIndex(adrs.getTreeIndex() / 2);
123+
node[1] = engine.H(pkSeed, adrs, node[0], authPath[j]);
124+
}
125+
else
126+
{
127+
adrs.setTreeIndex((adrs.getTreeIndex() - 1) / 2);
128+
node[1] = engine.H(pkSeed, adrs, authPath[j], node[0]);
129+
}
130+
node[0] = node[1];
131+
}
132+
root[i] = node[0];
133+
}
134+
ADRS forspkADRS = new ADRS(adrs); // copy address to create FTS public key address
135+
forspkADRS.setType(ADRS.FORS_PK);
136+
forspkADRS.setKeyPairAddress(adrs.getKeyPairAddress());
137+
return engine.T_l(pkSeed, forspkADRS, Arrays.concatenate(root));
138+
}
139+
140+
/**
141+
* Interprets m as SPX_FORS_HEIGHT-bit unsigned integers.
142+
* Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits.
143+
* Assumes indices has space for SPX_FORS_TREES integers.
144+
*/
145+
static int[] message_to_idxs(byte[] msg, int fors_trees, int fors_height)
146+
{
147+
int offset = 0;
148+
int[] idxs = new int[fors_trees];
149+
for (int i = 0; i < fors_trees; i++)
150+
{
151+
idxs[i] = 0;
152+
for (int j = 0; j < fors_height; j++)
153+
{
154+
idxs[i] ^= ((msg[offset >> 3] >> (offset & 0x7)) & 0x1) << j;
155+
offset++;
156+
}
157+
}
158+
return idxs;
159+
}
160+
}

0 commit comments

Comments
 (0)