1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18/**
19* @author Vladimir N. Molotkov, Stepan M. Mishura
20* @version $Revision$
21*/
22
23package org.apache.harmony.security.asn1;
24
25import java.io.IOException;
26import java.io.InputStream;
27import java.util.ArrayList;
28
29
30/**
31 * Decodes ASN.1 types encoded with BER (X.690)
32 *
33 * @see <a href="http://asn1.elibel.tm.fr/en/standards/index.htm">ASN.1</a>
34 */
35
36public class BerInputStream {
37
38    private final InputStream in;
39    protected byte[] buffer;
40
41    /**
42     * The position in the buffer.
43     * Next read must place data into the buffer from this offset
44     */
45    protected int offset = 0;
46
47    /**
48     * The buffer increment size.
49     * Must be reasonable big to reallocate memory not to often.
50     * Primary is used for decoding indefinite length encoding
51     */
52    private static final int BUF_INCREASE_SIZE = 1024 * 16;
53
54    /** Indicates indefinite length of the current type */
55    protected static final int INDEFINIT_LENGTH = -1;
56
57    /** Current decoded tag */
58    public int tag;
59
60    /** Current decoded length */
61    protected int length;
62
63    /** Current decoded content */
64    public Object content;
65
66    /** Current decoded tag offset */
67    protected int tagOffset;
68
69    /** Current decoded content offset */
70    protected int contentOffset;
71
72    /**
73     * Creates stream for decoding.
74     */
75    public BerInputStream(byte[] encoded) throws IOException {
76        this(encoded, 0, encoded.length);
77    }
78
79    /**
80     * Creates stream for decoding.
81     *
82     * @param encoded bytes array to be decoded
83     * @param offset the encoding offset
84     * @param expectedLength expected length of full encoding, this includes
85     *     identifier, length an content octets
86     */
87    public BerInputStream(byte[] encoded, int offset, int expectedLength) throws IOException {
88        this.in = null;
89        this.buffer = encoded;
90        this.offset = offset;
91
92        next();
93
94        // compare expected and decoded length
95        if (length != INDEFINIT_LENGTH
96                && (offset + expectedLength) != (this.offset + this.length)) {
97            throw new ASN1Exception("Wrong content length");
98        }
99    }
100
101    /**
102     * Creates stream for decoding.
103     *
104     * Allocates initial buffer of default size
105     */
106    public BerInputStream(InputStream in) throws IOException {
107        this(in, BUF_INCREASE_SIZE);
108    }
109
110    /**
111     * Creates stream for decoding.
112     *
113     * @param initialSize the internal buffer initial size
114     */
115    public BerInputStream(InputStream in, int initialSize) throws IOException {
116        this.in = in;
117        buffer = new byte[initialSize];
118
119        next();
120
121        if (length != INDEFINIT_LENGTH) {
122            // input stream has definite length encoding
123            // check allocated length to avoid further reallocations
124            if (buffer.length < (length + offset)) {
125                byte[] newBuffer = new byte[length + offset];
126                System.arraycopy(buffer, 0, newBuffer, 0, offset);
127                buffer = newBuffer;
128            }
129        } else {
130            isIndefinedLength = true;
131            throw new ASN1Exception("Decoding indefinite length encoding is not supported");
132        }
133    }
134
135    /**
136     * Resets this stream to initial state.
137     *
138     * @param encoded a new bytes array to be decoded
139     * @throws IOException if an error occurs
140     */
141    public final void reset(byte[] encoded) throws IOException {
142        buffer = encoded;
143        next();
144    }
145
146    /**
147     * Decodes next encoded type.
148     * Initializes tag, length, tagOffset and contentOffset variables
149     *
150     * @return next decoded tag
151     * @throws IOException if error occured
152     */
153    public int next() throws IOException {
154        tagOffset = offset;
155
156        // read tag
157        tag = read();
158
159        // read length
160        length = read();
161        if (length != 0x80) { // definite form
162            // long or short length form
163            if ((length & 0x80) != 0) { // long form
164                int numOctets = length & 0x7F;
165
166                if (numOctets > 5) {
167                    throw new ASN1Exception("Too long encoding at [" + tagOffset + "]"); //FIXME message
168                }
169
170                // collect this value length
171                length = read();
172                for (int i = 1; i < numOctets; i++) {
173                    int ch = read();
174                    length = (length << 8) + ch;//read();
175                }
176
177                if (length > 0xFFFFFF) {
178                    throw new ASN1Exception("Too long encoding at [" + tagOffset + "]"); //FIXME message
179                }
180            }
181        } else { //indefinite form
182            length = INDEFINIT_LENGTH;
183        }
184        contentOffset = offset;
185
186        return tag;
187    }
188
189    /**
190     * Returns the length of the encoding
191     */
192    public static int getLength(byte[] encoding) {
193        int length = encoding[1] & 0xFF;
194        int numOctets = 0;
195        if ((length & 0x80) != 0) { // long form
196            numOctets = length & 0x7F;
197
198            // collect this value length
199            length = encoding[2] & 0xFF;
200            for (int i = 3; i < numOctets + 2; i++) {
201                length = (length << 8) + (encoding[i] & 0xFF);
202            }
203        }
204        //    tag length long_form content
205        return 1 + 1 + numOctets + length;
206    }
207
208    /**
209     * Decodes ASN.1 bitstring type
210     */
211    public void readBitString() throws IOException {
212        if (tag == ASN1Constants.TAG_BITSTRING) {
213
214            if (length == 0) {
215                throw new ASN1Exception("ASN.1 Bitstring: wrong length. Tag at [" + tagOffset + "]");
216            }
217
218            readContent();
219
220            // content: check unused bits
221            if (buffer[contentOffset] > 7) {
222                throw new ASN1Exception("ASN.1 Bitstring: wrong content at [" + contentOffset
223                        + "]. A number of unused bits MUST be in range 0 to 7");
224            }
225
226            if (length == 1 && buffer[contentOffset] != 0) {
227                throw new ASN1Exception("ASN.1 Bitstring: wrong content at [" + contentOffset
228                        + "]. For empty string unused bits MUST be 0");
229            }
230
231        } else if (tag == ASN1Constants.TAG_C_BITSTRING) {
232            throw new ASN1Exception("Decoding constructed ASN.1 bitstring  type is not provided");
233        } else {
234            throw expected("bitstring");
235        }
236    }
237
238    /**
239     * Decodes ASN.1 Enumerated type
240     */
241    public void readEnumerated() throws IOException {
242        if (tag != ASN1Constants.TAG_ENUM) {
243            throw expected("enumerated");
244        }
245
246        // check encoded length
247        if (length == 0) {
248            throw new ASN1Exception("ASN.1 enumerated: wrong length for identifier at ["
249                    + tagOffset + "]");
250        }
251
252        readContent();
253
254        // check encoded content
255        if (length > 1) {
256            int bits = buffer[contentOffset] & 0xFF;
257            if (buffer[contentOffset + 1] < 0) {
258                bits += 0x100;
259            }
260
261            if (bits == 0 || bits == 0x1FF) {
262                throw new ASN1Exception("ASN.1 enumerated: wrong content at [" + contentOffset
263                        + "]. An integer MUST be encoded in minimum number of octets");
264            }
265        }
266    }
267
268    /**
269     * Decodes ASN.1 boolean type
270     */
271    public void readBoolean() throws IOException {
272        if (tag != ASN1Constants.TAG_BOOLEAN) {
273            throw expected("boolean");
274        }
275
276        // check encoded length
277        if (length != 1) {
278            throw new ASN1Exception("Wrong length for ASN.1 boolean at [" + tagOffset + "]");
279        }
280
281        readContent();
282    }
283
284    /** The last choice index */
285    public int choiceIndex;
286
287    /** Keeps last decoded: year, month, day, hour, minute, second, millisecond */
288    public int[] times;
289
290    /**
291     * Decodes ASN.1 GeneralizedTime type
292     *
293     * @throws IOException if error occured
294     */
295    public void readGeneralizedTime() throws IOException {
296        if (tag == ASN1Constants.TAG_GENERALIZEDTIME) {
297            // FIXME: any other optimizations?
298            readContent();
299            // FIXME store string somewhere to allow a custom time type perform
300            // additional checks
301
302            // check syntax: the last char MUST be Z
303            if (buffer[offset - 1] != 'Z') {
304                // FIXME support only format that is acceptable for DER
305                throw new ASN1Exception("ASN.1 GeneralizedTime: encoded format is not implemented");
306            }
307
308            // check syntax: MUST be YYYYMMDDHHMMSS[(./,)DDD]'Z'
309            if (length != 15 && (length < 17 || length > 19)) {
310                throw new ASN1Exception("ASN.1 GeneralizedTime wrongly encoded at ["
311                        + contentOffset + "]");
312            }
313
314            // check content: milliseconds
315            if (length > 16) {
316                byte char14 = buffer[contentOffset + 14];
317                if (char14 != '.' && char14 != ',') {
318                    throw new ASN1Exception("ASN.1 GeneralizedTime wrongly encoded at ["
319                            + contentOffset + "]");
320                }
321            }
322
323            if (times == null) {
324                times = new int[7];
325            }
326            times[0] = strToInt(contentOffset, 4); // year
327            times[1] = strToInt(contentOffset + 4, 2); // month
328            times[2] = strToInt(contentOffset + 6, 2); // day
329            times[3] = strToInt(contentOffset + 8, 2); // hour
330            times[4] = strToInt(contentOffset + 10, 2); // minute
331            times[5] = strToInt(contentOffset + 12, 2); // second
332
333            if (length > 16) {
334                // FIXME optimize me
335                times[6] = strToInt(contentOffset + 15, length - 16);
336
337                if (length == 17) {
338                    times[6] = times[6] * 100;
339                } else if (length == 18) {
340                    times[6] = times[6] * 10;
341                }
342            }
343
344            // FIXME check all values for valid numbers!!!
345        } else if (tag == ASN1Constants.TAG_C_GENERALIZEDTIME) {
346            throw new ASN1Exception("Decoding constructed ASN.1 GeneralizedTime type is not supported");
347        } else {
348            throw expected("GeneralizedTime");
349        }
350    }
351
352    /**
353     * Decodes ASN.1 UTCTime type
354     *
355     * @throws IOException if an I/O error occurs or the end of the stream is reached
356     */
357    public void readUTCTime() throws IOException {
358        if (tag == ASN1Constants.TAG_UTCTIME) {
359            switch (length) {
360            case ASN1UTCTime.UTC_HM:
361            case ASN1UTCTime.UTC_HMS:
362                break;
363            case ASN1UTCTime.UTC_LOCAL_HM:
364            case ASN1UTCTime.UTC_LOCAL_HMS:
365                // FIXME only coordinated universal time formats are supported
366                throw new ASN1Exception("ASN.1 UTCTime: local time format is not supported");
367            default:
368                throw new ASN1Exception("ASN.1 UTCTime: wrong length, identifier at " + tagOffset);
369            }
370
371            // FIXME: any other optimizations?
372            readContent();
373
374            // FIXME store string somewhere to allow a custom time type perform
375            // additional checks
376
377            // check syntax: the last char MUST be Z
378            if (buffer[offset - 1] != 'Z') {
379                throw new ASN1Exception("ASN.1 UTCTime wrongly encoded at ["
380                        + contentOffset + ']');
381            }
382
383            if (times == null) {
384                times = new int[7];
385            }
386
387            times[0] = strToInt(contentOffset, 2); // year
388            if (times[0] > 49) {
389                times[0] += 1900;
390            } else {
391                times[0] += 2000;
392            }
393
394            times[1] = strToInt(contentOffset + 2, 2); // month
395            times[2] = strToInt(contentOffset + 4, 2); // day
396            times[3] = strToInt(contentOffset + 6, 2); // hour
397            times[4] = strToInt(contentOffset + 8, 2); // minute
398
399            if (length == ASN1UTCTime.UTC_HMS) {
400                times[5] = strToInt(contentOffset + 10, 2); // second
401            }
402
403            // FIXME check all time values for valid numbers!!!
404        } else if (tag == ASN1Constants.TAG_C_UTCTIME) {
405            throw new ASN1Exception("Decoding constructed ASN.1 UTCTime type is not supported");
406        } else {
407            throw expected("UTCTime");
408        }
409    }
410
411    private int strToInt(int off, int count) throws ASN1Exception {
412        int result = 0;
413        for (int i = off, end = off + count; i < end; i++) {
414            int c = buffer[i] - 48;
415            if (c < 0 || c > 9) {
416                throw new ASN1Exception("Time encoding has invalid char");
417            }
418            result = result * 10 + c;
419        }
420        return result;
421    }
422
423    /**
424     * Decodes ASN.1 Integer type
425     */
426    public void readInteger() throws IOException {
427        if (tag != ASN1Constants.TAG_INTEGER) {
428            throw expected("integer");
429        }
430
431        // check encoded length
432        if (length < 1) {
433            throw new ASN1Exception("Wrong length for ASN.1 integer at [" + tagOffset + "]");
434        }
435
436        readContent();
437
438        // check encoded content
439        if (length > 1) {
440            byte firstByte = buffer[offset - length];
441            byte secondByte = (byte) (buffer[offset - length + 1] & 0x80);
442
443            if (firstByte == 0 && secondByte == 0 || firstByte == (byte) 0xFF
444                    && secondByte == (byte) 0x80) {
445                throw new ASN1Exception("Wrong content for ASN.1 integer at [" + (offset - length) + "]. An integer MUST be encoded in minimum number of octets");
446            }
447        }
448    }
449
450    /**
451     * Decodes ASN.1 Octetstring type
452     */
453    public void readOctetString() throws IOException {
454        if (tag == ASN1Constants.TAG_OCTETSTRING) {
455            readContent();
456        } else if (tag == ASN1Constants.TAG_C_OCTETSTRING) {
457            throw new ASN1Exception("Decoding constructed ASN.1 octet string type is not supported");
458        } else {
459            throw expected("octetstring");
460        }
461    }
462
463    private ASN1Exception expected(String what) throws ASN1Exception {
464        throw new ASN1Exception("ASN.1 " + what + " identifier expected at [" + tagOffset + "], got " + Integer.toHexString(tag));
465    }
466
467    public int oidElement;
468
469    /**
470     * Decodes ASN.1 ObjectIdentifier type
471     */
472    public void readOID() throws IOException {
473        if (tag != ASN1Constants.TAG_OID) {
474            throw expected("OID");
475        }
476
477        // check encoded length
478        if (length < 1) {
479            throw new ASN1Exception("Wrong length for ASN.1 object identifier at [" + tagOffset + "]");
480        }
481
482        readContent();
483
484        // check content: last encoded byte (8th bit MUST be zero)
485        if ((buffer[offset - 1] & 0x80) != 0) {
486            throw new ASN1Exception("Wrong encoding at [" + (offset - 1) + "]");
487        }
488
489        oidElement = 1;
490        for (int i = 0; i < length; i++, ++oidElement) {
491            while ((buffer[contentOffset + i] & 0x80) == 0x80) {
492                i++;
493            }
494        }
495    }
496
497    /**
498     * Decodes ASN.1 Sequence type
499     */
500    public void readSequence(ASN1Sequence sequence) throws IOException {
501        if (tag != ASN1Constants.TAG_C_SEQUENCE) {
502            throw expected("sequence");
503        }
504
505        int begOffset = offset;
506        int endOffset = begOffset + length;
507
508        ASN1Type[] type = sequence.type;
509
510        int i = 0;
511
512        if (isVerify) {
513
514            for (; (offset < endOffset) && (i < type.length); i++) {
515
516                next();
517                while (!type[i].checkTag(tag)) {
518                    // check whether it is optional component or not
519                    if (!sequence.OPTIONAL[i] || (i == type.length - 1)) {
520                        throw new ASN1Exception("ASN.1 Sequence: mandatory value is missing at [" + tagOffset + "]");
521                    }
522                    i++;
523                }
524
525                type[i].decode(this);
526            }
527
528            // check the rest of components
529            for (; i < type.length; i++) {
530                if (!sequence.OPTIONAL[i]) {
531                    throw new ASN1Exception("ASN.1 Sequence: mandatory value is missing at [" + tagOffset + "]");
532                }
533            }
534
535        } else {
536            int seqTagOffset = tagOffset; //store tag offset
537
538            Object[] values = new Object[type.length];
539            for (; (offset < endOffset) && (i < type.length); i++) {
540
541                next();
542                while (!type[i].checkTag(tag)) {
543                    // check whether it is optional component or not
544                    if (!sequence.OPTIONAL[i] || (i == type.length - 1)) {
545                        throw new ASN1Exception("ASN.1 Sequence: mandatory value is missing at [" + tagOffset + "]");
546                    }
547
548                    // sets default value
549                    if (sequence.DEFAULT[i] != null) {
550                        values[i] = sequence.DEFAULT[i];
551                    }
552                    i++;
553                }
554                values[i] = type[i].decode(this);
555            }
556
557            // check the rest of components
558            for (; i < type.length; i++) {
559                if (!sequence.OPTIONAL[i]) {
560                    throw new ASN1Exception("ASN.1 Sequence: mandatory value is missing at [" + tagOffset + "]");
561                }
562                if (sequence.DEFAULT[i] != null) {
563                    values[i] = sequence.DEFAULT[i];
564                }
565            }
566            content = values;
567
568            tagOffset = seqTagOffset; //retrieve tag offset
569        }
570
571        if (offset != endOffset) {
572            throw new ASN1Exception("Wrong encoding at [" + begOffset + "]. Content's length and encoded length are not the same");
573        }
574    }
575
576    /**
577     * Decodes ASN.1 SequenceOf type
578     */
579    public void readSequenceOf(ASN1SequenceOf sequenceOf) throws IOException {
580        if (tag != ASN1Constants.TAG_C_SEQUENCEOF) {
581            throw expected("sequenceOf");
582        }
583
584        decodeValueCollection(sequenceOf);
585    }
586
587    /**
588     * Decodes ASN.1 Set type
589     */
590    public void readSet(ASN1Set set) throws IOException {
591        if (tag != ASN1Constants.TAG_C_SET) {
592            throw expected("set");
593        }
594
595        throw new ASN1Exception("Decoding ASN.1 Set type is not supported");
596    }
597
598    /**
599     * Decodes ASN.1 SetOf type
600     */
601    public void readSetOf(ASN1SetOf setOf) throws IOException {
602        if (tag != ASN1Constants.TAG_C_SETOF) {
603            throw expected("setOf");
604        }
605
606        decodeValueCollection(setOf);
607    }
608
609    private void decodeValueCollection(ASN1ValueCollection collection) throws IOException {
610        int begOffset = offset;
611        int endOffset = begOffset + length;
612
613        ASN1Type type = collection.type;
614
615        if (isVerify) {
616            while (endOffset > offset) {
617                next();
618                type.decode(this);
619            }
620        } else {
621            int seqTagOffset = tagOffset; //store tag offset
622
623            ArrayList<Object> values = new ArrayList<Object>();
624            while (endOffset > offset) {
625                next();
626                values.add(type.decode(this));
627            }
628
629            values.trimToSize();
630            content = values;
631
632            tagOffset = seqTagOffset; //retrieve tag offset
633        }
634
635        if (offset != endOffset) {
636            throw new ASN1Exception("Wrong encoding at [" + begOffset + "]. Content's length and encoded length are not the same");
637        }
638    }
639
640    /**
641     * Decodes ASN.1 String type
642     *
643     * @throws IOException if an I/O error occurs or the end of the stream is reached
644     */
645    public void readString(ASN1StringType type) throws IOException {
646        if (tag == type.id) {
647            readContent();
648        } else if (tag == type.constrId) {
649            throw new ASN1Exception("Decoding constructed ASN.1 string type is not provided");
650        } else {
651            throw expected("string");
652        }
653    }
654
655    /**
656     * Returns encoded array.
657     *
658     * MUST be invoked after decoding corresponding ASN.1 notation
659     */
660    public byte[] getEncoded() {
661        byte[] encoded = new byte[offset - tagOffset];
662        System.arraycopy(buffer, tagOffset, encoded, 0, encoded.length);
663        return encoded;
664    }
665
666    /**
667     * Returns internal buffer used for decoding
668     */
669    public final byte[] getBuffer() {
670        return buffer;
671    }
672
673    /**
674     * Returns length of the current content for decoding
675     */
676    public final int getLength() {
677        return length;
678    }
679
680    /**
681     * Returns the current offset
682     */
683    public final int getOffset() {
684        return offset;
685    }
686
687    /**
688     * Returns end offset for the current encoded type
689     */
690    public final int getEndOffset() {
691        return offset + length;
692    }
693
694    /**
695     * Returns start offset for the current encoded type
696     */
697    public final int getTagOffset() {
698        return tagOffset;
699    }
700
701    /**
702     * Indicates verify or store mode.
703     *
704     * In store mode a decoded content is stored in a newly allocated
705     * appropriate object. The <code>content</code> variable holds
706     * a reference to the last created object.
707     *
708     * In verify mode a decoded content is not stored.
709     */
710    // FIXME it is used only for one case
711    // decoding PCKS#8 Private Key Info notation
712    // remove this option because it does decoding more complex
713    protected boolean isVerify;
714
715    /**
716     * Sets verify mode.
717     */
718    public final void setVerify() {
719        isVerify = true;
720    }
721
722    /**
723     * Indicates defined or indefined reading mode for associated InputStream.
724     *
725     * This mode is defined by reading a length
726     * for a first ASN.1 type from InputStream.
727     */
728    protected boolean isIndefinedLength;
729
730    /**
731     * Reads the next encoded byte from the encoded input stream.
732     */
733    protected int read() throws IOException {
734        if (offset == buffer.length) {
735            throw new ASN1Exception("Unexpected end of encoding");
736        }
737
738        if (in == null) {
739            return buffer[offset++] & 0xFF;
740        } else {
741            int octet = in.read();
742            if (octet == -1) {
743                throw new ASN1Exception("Unexpected end of encoding");
744            }
745
746            buffer[offset++] = (byte) octet;
747
748            return octet;
749        }
750    }
751
752    /**
753     * Reads the next encoded content from the encoded input stream.
754     * The method MUST be used for reading a primitive encoded content.
755     */
756    public void readContent() throws IOException {
757        if (offset + length > buffer.length) {
758            throw new ASN1Exception("Unexpected end of encoding");
759        }
760
761        if (in == null) {
762            offset += length;
763        } else {
764            int bytesRead = in.read(buffer, offset, length);
765
766            if (bytesRead != length) {
767                // if input stream didn't return all data at once
768                // try to read it in several blocks
769                int c = bytesRead;
770                do {
771                    if (c < 1 || bytesRead > length) {
772                        throw new ASN1Exception("Failed to read encoded content");
773                    }
774                    c = in.read(buffer, offset + bytesRead, length - bytesRead);
775                    bytesRead += c;
776                } while (bytesRead != length);
777            }
778
779            offset += length;
780        }
781    }
782
783    /**
784     * Reallocates the buffer in order to make it
785     * exactly the size of data it contains
786     */
787    public void compactBuffer() {
788        if (offset != buffer.length) {
789            byte[] newBuffer = new byte[offset];
790            // restore buffer content
791            System.arraycopy(buffer, 0, newBuffer, 0, offset);
792            // set new buffer
793            buffer = newBuffer;
794        }
795    }
796    private Object[][] pool;
797
798    public void put(Object key, Object entry) {
799        if (pool == null) {
800            pool = new Object[2][10];
801        }
802
803        int i = 0;
804        for (; i < pool[0].length && pool[0][i] != null; i++) {
805            if (pool[0][i] == key) {
806                pool[1][i] = entry;
807                return;
808            }
809        }
810
811        if (i == pool[0].length) {
812            Object[][] newPool = new Object[pool[0].length * 2][2];
813            System.arraycopy(pool[0], 0, newPool[0], 0, pool[0].length);
814            System.arraycopy(pool[1], 0, newPool[1], 0, pool[0].length);
815            pool = newPool;
816        } else {
817            pool[0][i] = key;
818            pool[1][i] = entry;
819        }
820    }
821
822    public Object get(Object key) {
823        if (pool == null) {
824            return null;
825        }
826
827        for (int i = 0; i < pool[0].length; i++) {
828            if (pool[0][i] == key) {
829                return pool[1][i];
830            }
831        }
832        return null;
833    }
834}
835