001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2017 ForgeRock AS
015 */
016
017package org.forgerock.json.jose.utils;
018
019import java.math.BigInteger;
020
021import org.forgerock.util.encode.Base64url;
022
023/** Utils to complement bit operations not covered by the BigInteger functions. */
024public final class BigIntegerUtils {
025
026    private BigIntegerUtils() {
027    }
028
029    /**
030     * Returns the magnitude big-endian byte array of a big integer.
031     * @param x a big integer
032     * @return the magnitude big-endian byte array of x
033     */
034    public static byte[] toBytesUnsigned(final BigInteger x) {
035        /*
036            we got a binary number represented in multiple octets, and we want to take only the k first bits, with here
037            k = x.bitLength();
038            As it is a positive number, the last byte will be completed by 0 bits.
039
040            x = 00000011 10111101 10011111 and k = 18
041            Let's write x with references: x = A(00000011) B(10111101) C(10011111)
042            A,B,C are bytes.
043
044            With k=18, it means the unsigned big endian representation would be the first 18 bits,
045            so 11 10111101 10011111
046            As we have to store it in byte array, we will have to complete with zero anyway, resulting to:
047            00000011 10111101 10011111 which is x in this case but not always:
048
049            The limit case when k % 8 = 0, which is called byte-aligned, will have a different result. As x contains the
050            sign too, it will need to add an extra byte to represent the sign. For example:
051            For the positive number 11011000 11100000, so k = 16, x would be 00000000 11011000 11100000
052            The extra 0 bits are here to indicates it's indeed a positive number.
053            For our problem, we need to skip the extra 0 and returning 11011000 11100000
054
055         */
056        final byte[] xBytes = x.abs().toByteArray();
057
058        //As explained earlier, we can return the xbytes if this is not the byte-align case
059        if (xBytes.length == 0 || xBytes[0] != 0x00) {
060            return xBytes;
061        }
062        final byte[] unsignedBigEndian = new byte[xBytes.length - 1];
063        System.arraycopy(xBytes, 1, unsignedBigEndian, 0, xBytes.length - 1);
064        return unsignedBigEndian;
065    }
066
067    /**
068     * Decode a big-endian base64 url encoding of a magnitude big integer and transform it as a positive big integer.
069     * @param magnitudeBase64UrlEncoded  big-endian base64 url encoding of a big integer magnitude
070     * @return a positive big integer with the magnitude decoded from thhe
071     */
072    public static BigInteger base64UrlDecode(String magnitudeBase64UrlEncoded) {
073        final int positive = 1;
074        return new BigInteger(positive, Base64url.decode(magnitudeBase64UrlEncoded));
075    }
076}