Wentao Shang | 882e34e | 2013-01-05 02:49:51 -0800 | [diff] [blame] | 1 | /** |
| 2 | * @author: Wentao Shang |
| 3 | * See COPYING for copyright and distribution information. |
| 4 | */ |
| 5 | |
| 6 | var MerklePath = function MerkelPath() { |
| 7 | this.index = null; // int |
| 8 | this.digestList = []; // array of hex string |
| 9 | }; |
| 10 | |
| 11 | var Witness = function Witness() { |
| 12 | this.oid = null; // string |
| 13 | this.path = new MerklePath(); // MerklePath |
| 14 | }; |
| 15 | |
Wentao Shang | e0d7f05 | 2013-01-05 16:37:02 -0800 | [diff] [blame] | 16 | function parseOID(bytes, start, end) { |
| 17 | var s, n = 0, bits = 0; |
| 18 | for (var i = start; i < end; ++i) { |
| 19 | var v = bytes[i]; |
| 20 | n = (n << 7) | (v & 0x7F); |
| 21 | bits += 7; |
| 22 | if (!(v & 0x80)) { // finished |
| 23 | if (s == undefined) |
| 24 | s = parseInt(n / 40) + "." + (n % 40); |
| 25 | else |
| 26 | s += "." + ((bits >= 31) ? "bigint" : n); |
| 27 | n = bits = 0; |
| 28 | } |
| 29 | s += String.fromCharCode(); |
| 30 | } |
| 31 | return s; |
| 32 | } |
| 33 | |
| 34 | function parseInteger(bytes, start, end) { |
| 35 | var n = 0; |
| 36 | for (var i = start; i < end; ++i) |
| 37 | n = (n << 8) | bytes[i]; |
| 38 | return n; |
| 39 | } |
| 40 | |
Wentao Shang | 882e34e | 2013-01-05 02:49:51 -0800 | [diff] [blame] | 41 | Witness.prototype.decode = function(/* Uint8Array */ witness) { |
Wentao Shang | e0d7f05 | 2013-01-05 16:37:02 -0800 | [diff] [blame] | 42 | /* The asn1.js decoder has some bug and |
| 43 | * cannot decode certain kind of witness. |
| 44 | * So we use an alternative (and dirty) hack |
| 45 | * to read witness from byte streams |
| 46 | * ------Wentao |
| 47 | */ |
| 48 | /* |
Wentao Shang | 882e34e | 2013-01-05 02:49:51 -0800 | [diff] [blame] | 49 | var wit = DataUtils.toHex(witness).toLowerCase(); |
Wentao Shang | e0d7f05 | 2013-01-05 16:37:02 -0800 | [diff] [blame] | 50 | try { |
| 51 | var der = Hex.decode(wit); |
| 52 | var asn1 = ASN1.decode(der); |
| 53 | } |
| 54 | catch (e) { |
| 55 | console.log(e); |
| 56 | console.log(wit); |
| 57 | } |
Wentao Shang | 882e34e | 2013-01-05 02:49:51 -0800 | [diff] [blame] | 58 | //console.log(asn1.toPrettyString()); |
| 59 | |
| 60 | this.oid = asn1.sub[0].sub[0].content(); // OID |
Wentao Shang | e0d7f05 | 2013-01-05 16:37:02 -0800 | [diff] [blame] | 61 | //console.log(this.oid); |
Wentao Shang | 882e34e | 2013-01-05 02:49:51 -0800 | [diff] [blame] | 62 | this.path.index = asn1.sub[1].sub[0].sub[0].content(); // index |
Wentao Shang | e0d7f05 | 2013-01-05 16:37:02 -0800 | [diff] [blame] | 63 | //console.log(this.path.index); |
Wentao Shang | 882e34e | 2013-01-05 02:49:51 -0800 | [diff] [blame] | 64 | for (i = 0; i < asn1.sub[1].sub[0].sub[1].sub.length; i++) { |
| 65 | pos = asn1.sub[1].sub[0].sub[1].sub[i].stream.pos; |
| 66 | str = wit.substring(2 * pos + 4, 2 * pos + 68); |
| 67 | this.path.digestList.push(str); // digest hex string |
Wentao Shang | e0d7f05 | 2013-01-05 16:37:02 -0800 | [diff] [blame] | 68 | //console.log(str); |
| 69 | } |
| 70 | */ |
| 71 | |
| 72 | // FIXME: Need to be fixed to support arbitrary ASN1 encoding, |
| 73 | // But do we really nned that???? -------Wentao |
| 74 | |
| 75 | // The structure of Witness is fixed as follows: |
| 76 | // SEQUENCE (2 elem) |
| 77 | // SEQUENCE (1 elem) |
| 78 | // OBJECT IDENTIFIER 1.2.840.113550.11.1.2.2 |
| 79 | // OCTET STRING (1 elem) |
| 80 | // SEQUENCE (2 elem) |
| 81 | // INTEGER index |
| 82 | // SEQUENCE (n elem) |
| 83 | // OCTET STRING(32 byte) 345FB4B5E9A1D2FF450ECA87EB87601683027A1A... |
| 84 | // OCTET STRING(32 byte) DBCEE5B7A6C2B851B029324197DDBD9A655723DC... |
| 85 | // OCTET STRING(32 byte) 4C79B2D256E4CD657A27F01DCB51AC3C56A24E71... |
| 86 | // OCTET STRING(32 byte) 7F7FB169604A87EAC94378F0BDB4FC5D5899AB88... |
| 87 | // ...... |
| 88 | // Hence we can follow this structure to extract witness fields at fixed level |
| 89 | // Tag numbers for ASN1: |
| 90 | // SEQUENCE 0x10 |
| 91 | // OCT STRING 0x04 |
| 92 | // INTEGER 0x02 |
| 93 | // OBJECT IDENTIFIER 0x06 |
| 94 | var i = 0; |
| 95 | var step = 0; // count of sequence tag |
| 96 | while (i < witness.length) { |
| 97 | var len = 0; |
| 98 | |
| 99 | if (witness[i] == 0x30) { |
| 100 | // Sequence (constructed) |
| 101 | // There is no primitive sequence in Witness |
| 102 | if ((witness[i + 1] & 0x80) != 0) { |
| 103 | len = witness[i+1] & 0x7F; |
| 104 | } |
| 105 | step++; |
| 106 | } else if (witness[i] == 0x06) { |
| 107 | // Decode OID |
| 108 | len = witness[i+1]; // XXX: OID will not be longer than 127 bytes |
| 109 | this.oid = parseOID(witness, i + 2, i + 2 + len); |
| 110 | //console.log(this.oid); |
| 111 | } else if (witness[i] == 0x02) { |
| 112 | // Decode node index |
| 113 | len = witness[i+1]; // XXX: index will not be longer than 127 bytes |
| 114 | this.path.index = parseInteger(witness, i + 2, i + 2 + len); |
| 115 | //console.log(this.path.index); |
| 116 | } else if (witness[i] == 0x04) { |
| 117 | if ((witness[i + 1] & 0x80) != 0) { |
| 118 | len = witness[i+1] & 0x7F; |
Wentao Shang | e0d7f05 | 2013-01-05 16:37:02 -0800 | [diff] [blame] | 119 | } |
| 120 | if (step == 4) { |
| 121 | // Start to decode digest hex string |
Wentao Shang | edd4dea | 2013-01-19 16:55:11 -0800 | [diff] [blame] | 122 | len = witness[i+1]; // XXX: digest hex should always be 32 bytes |
Jeff Thompson | 48ff28a | 2013-02-18 22:53:29 -0800 | [diff] [blame] | 123 | var str = DataUtils.toHex(witness.subarray(i + 2, i + 2 + len)); |
Wentao Shang | e0d7f05 | 2013-01-05 16:37:02 -0800 | [diff] [blame] | 124 | this.path.digestList.push(str); // digest hex string |
| 125 | //console.log(str); |
| 126 | } |
| 127 | } |
| 128 | i = i + 2 + len; |
Wentao Shang | 882e34e | 2013-01-05 02:49:51 -0800 | [diff] [blame] | 129 | } |
| 130 | }; |