1/*
2 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.java2d.cmm;
27
28public class ProfileDataVerifier {
29    /**
30     * Throws an IllegalArgumentException if the data does not correspond
31     * to a valid ICC Profile.
32     *
33     * @param data the specified profile data.
34     */
35    public static void verify(byte[] data) {
36        if (data == null) {
37            throw new IllegalArgumentException("Invalid ICC Profile Data");
38        }
39
40        if (data.length < TOC_OFFSET) {
41            // not enough data for profile header
42            throw new IllegalArgumentException("Invalid ICC Profile Data");
43        }
44
45        // check profile size
46        final int size = readInt32(data, 0);
47        final int tagCount = readInt32(data, HEADER_SIZE);
48
49        if (tagCount < 0 || tagCount > MAX_TAG_COUNT) {
50            throw new IllegalArgumentException("Invalid ICC Profile Data");
51        }
52
53        if (size < (TOC_OFFSET + (tagCount * TOC_RECORD_SIZE)) ||
54            size > data.length)
55        {
56            throw new IllegalArgumentException("Invalid ICC Profile Data");
57        }
58
59        final int sig = readInt32(data, 36);
60
61        if (PROFILE_FILE_SIGNATURE != sig) {
62            throw new IllegalArgumentException("Invalid ICC Profile Data");
63        }
64
65        // verify table of content
66        for (int i = 0; i < tagCount; i++) {
67            final int tag_offset = getTagOffset(i, data);
68            final int tag_size = getTagSize(i, data);
69
70            if (tag_offset < TOC_OFFSET || tag_offset > size) {
71                throw new IllegalArgumentException("Invalid ICC Profile Data");
72            }
73
74            if (tag_size < 0 ||
75                tag_size > (Integer.MAX_VALUE - tag_offset) ||
76                tag_size + tag_offset > size)
77            {
78                throw new IllegalArgumentException("Invalid ICC Profile Data");
79            }
80        }
81    }
82
83    private static int getTagOffset(int idx, byte[] data) {
84        final int pos = TOC_OFFSET + idx * TOC_RECORD_SIZE + 4;
85        return readInt32(data, pos);
86    }
87
88    private static int getTagSize(int idx, byte[] data) {
89        final int pos = TOC_OFFSET + idx * TOC_RECORD_SIZE + 8;
90        return readInt32(data, pos);
91    }
92
93    private static int readInt32(byte[] data, int off) {
94        int res = 0;
95        for (int i = 0; i < 4; i++) {
96            res = res << 8;
97
98            res |= (0xff & data[off++]);
99        }
100        return res;
101    }
102
103    /**
104     * Lcms limit for the number of tags: 100
105     * Kcms limit for the number of tags: N/A
106     */
107    private static final int MAX_TAG_COUNT = 100;
108
109    private static final int HEADER_SIZE = 128;
110    private static final int TOC_OFFSET = HEADER_SIZE + 4;
111    private static final int TOC_RECORD_SIZE = 12;
112
113    private static final int PROFILE_FILE_SIGNATURE = 0x61637370;
114}
115