summaryrefslogtreecommitdiff
path: root/src/org/noreply/fancydress/type3/SURB.java
blob: 67339094f90114505a3c46beda87816461d6deb6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
/* $Id$ */
package org.noreply.fancydress.type3;

import org.noreply.fancydress.type3.routing.*;
import org.noreply.fancydress.status.*;
import org.noreply.fancydress.crypto.*;
import org.noreply.fancydress.misc.*;

public class SURB extends SingleLeg {
	public static final byte[] VERSION = {0, 1};
	public static final String ARMOR_VERSION = "0.2";
	private byte[] sharedSecret;

	public SURB (HalfPath path, byte[] secret, RoutingDestination address) throws Mix3BadArgumentsChainTooLongException {
		super();
		byte[] seed;
		byte[] validateHash;
		byte[] validate = Util.toOctets("Validate");

		Hop[] hops = path.getHops();

		do {
			seed = CryptoPrimitives.rand(20);
			seed[0] &= 0x7f;
			validateHash = CryptoPrimitives.hash(seed, secret, validate);
		} while (validateHash[validateHash.length-1] == 0);

		byte[] key = Util.slice(CryptoPrimitives.hash(seed, secret, Util.toOctets("Generate")), 0, CryptoPrimitives.KEY_LEN);
		byte[] stream = CryptoPrimitives.prng(key, CryptoPrimitives.KEY_LEN*(hops.length + 1));

		sharedSecret = Util.slice(stream, CryptoPrimitives.KEY_LEN*hops.length, CryptoPrimitives.KEY_LEN);
		byte[][] sharedKeys = new byte[hops.length][];
		for (int i=0; i<hops.length; i++)
			sharedKeys[i] = Util.slice(stream, (hops.length-i-1)*CryptoPrimitives.KEY_LEN, CryptoPrimitives.KEY_LEN);

		address.setDecodingHandle(seed);
		makeLeg(hops, sharedKeys, address);
	};

    /*
      Begin Marker: 4 octets
      Version:      2 octets
      Use-by-Date:  4 octets
      SURB header:  2048 octets
      Routing Size: 2 octets
      Routing Type: 2 octets
      Encryption key: 16 octets
      Routing Info: (Routing Size) octets
     */

	public String export() {
		byte[] beginMarker = Util.toOctets("SURB");
		int useBy = 1068595200; //FIXME // Y2k36
		RoutingForward route = getRoute().asSwap();
		byte[] routingInformation = route.getRoutingInformation();
		int routingSize = routingInformation.length;
		int routingType = route.getRoutingType();

		byte[] result = new byte[4+2+4+SINGLE_HEADER_LEN+2+2+CryptoPrimitives.KEY_LEN+routingSize];

		int pos = 0;
		System.arraycopy(beginMarker, 0, result,  pos, beginMarker.length);
		pos += beginMarker.length;
		System.arraycopy(VERSION, 0, result,  pos, VERSION.length);
		pos += VERSION.length;
		result[pos] = (byte) ( (useBy >>24) & 0xff);
		pos++;
		result[pos] = (byte) ( (useBy >>16) & 0xff);
		pos++;
		result[pos] = (byte) ( (useBy >> 8) & 0xff);
		pos++;
		result[pos] = (byte) ( (useBy     ) & 0xff);
		pos++;
		System.arraycopy(super.asOctets(), 0, result,  pos, SINGLE_HEADER_LEN);
		pos += SINGLE_HEADER_LEN;
		result[pos] = (byte) ( (routingSize >> 8) & 0xff);
		pos++;
		result[pos] = (byte) (  routingSize       & 0xff);
		pos++;
		result[pos] = (byte) ( (routingType >> 8) & 0xff);
		pos++;
		result[pos] = (byte) (  routingType       & 0xff);
		pos++;
		System.arraycopy(sharedSecret, 0, result,  pos, CryptoPrimitives.KEY_LEN);
		pos += CryptoPrimitives.KEY_LEN;
		System.arraycopy(routingInformation, 0, result,  pos, routingSize);
		pos += routingSize;

		if (pos != result.length)
			throw new Error("did not fill in expected number of bytes");

		String[] headers = new String[1];
		headers[0] = "Version: "+ARMOR_VERSION;
		return Util.armor(result, "TYPE III REPLY BLOCK", headers);
	}
}