001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.kernel.security;
016    
017    import com.liferay.portal.kernel.io.BigEndianCodec;
018    import com.liferay.portal.kernel.util.GetterUtil;
019    
020    import java.security.SecureRandom;
021    
022    import java.util.Random;
023    import java.util.concurrent.atomic.AtomicBoolean;
024    import java.util.concurrent.atomic.AtomicInteger;
025    
026    /**
027     * @author Shuyang Zhou
028     */
029    public class SecureRandomUtil {
030    
031            public static boolean nextBoolean() {
032                    byte b = nextByte();
033    
034                    if (b < 0) {
035                            return false;
036                    }
037                    else {
038                            return true;
039                    }
040            }
041    
042            public static byte nextByte() {
043                    int index = _index.getAndIncrement();
044    
045                    if (index < _BUFFER_SIZE) {
046                            return _bytes[index];
047                    }
048    
049                    return (byte)_reload();
050            }
051    
052            public static double nextDouble() {
053                    int index = _index.getAndAdd(8);
054    
055                    if ((index + 7) < _BUFFER_SIZE) {
056                            return BigEndianCodec.getDouble(_bytes, index);
057                    }
058    
059                    return Double.longBitsToDouble(_reload());
060            }
061    
062            public static float nextFloat() {
063                    int index = _index.getAndAdd(4);
064    
065                    if ((index + 3) < _BUFFER_SIZE) {
066                            return BigEndianCodec.getFloat(_bytes, index);
067                    }
068    
069                    return Float.intBitsToFloat((int)_reload());
070            }
071    
072            public static int nextInt() {
073                    int index = _index.getAndAdd(4);
074    
075                    if ((index + 3) < _BUFFER_SIZE) {
076                            return BigEndianCodec.getInt(_bytes, index);
077                    }
078    
079                    return (int)_reload();
080            }
081    
082            public static long nextLong() {
083                    int index = _index.getAndAdd(8);
084    
085                    if ((index + 7) < _BUFFER_SIZE) {
086                            return BigEndianCodec.getLong(_bytes, index);
087                    }
088    
089                    return _reload();
090            }
091    
092            private static long _reload() {
093                    if (_reloadingFlag.compareAndSet(false, true)) {
094                            _random.nextBytes(_bytes);
095    
096                            _index.set(0);
097    
098                            _reloadingFlag.set(false);
099                    }
100    
101                    int offset = _index.get() % (_BUFFER_SIZE - 7);
102    
103                    long l = BigEndianCodec.getLong(_bytes, offset) ^ _gapSeed;
104    
105                    _gapSeed = l;
106    
107                    return l;
108            }
109    
110            private static final int _BUFFER_SIZE;
111    
112            private static final int _MIN_BUFFER_SIZE = 1024;
113    
114            private static final byte[] _bytes;
115            private static final AtomicInteger _index = new AtomicInteger();
116            private static final Random _random = new SecureRandom();
117            private static final AtomicBoolean _reloadingFlag = new AtomicBoolean();
118    
119            static {
120                    int bufferSize = GetterUtil.getInteger(
121                            System.getProperty(
122                                    SecureRandomUtil.class.getName() + ".buffer.size"));
123    
124                    if (bufferSize < _MIN_BUFFER_SIZE) {
125                            bufferSize = _MIN_BUFFER_SIZE;
126                    }
127    
128                    _BUFFER_SIZE = bufferSize;
129    
130                    _bytes = new byte[_BUFFER_SIZE];
131    
132                    _random.nextBytes(_bytes);
133    
134                    _gapSeed = _random.nextLong();
135            }
136    
137            private static long _gapSeed;
138    
139    }