001/*
002 * nimbus-jose-jwt
003 *
004 * Copyright 2012-2016, Connect2id Ltd and contributors.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.jose.crypto.utils;
019
020
021import java.math.BigInteger;
022import java.security.interfaces.ECPrivateKey;
023import java.security.interfaces.ECPublicKey;
024import java.security.spec.ECFieldFp;
025import java.security.spec.ECParameterSpec;
026import java.security.spec.ECPoint;
027import java.security.spec.EllipticCurve;
028
029
030/**
031 * Elliptic curve checks.
032 *
033 * @author Vladimir Dzhuvinov
034 * @version 2017-04-13
035 */
036public class ECChecks {
037        
038        
039        /**
040         * Checks if the specified (ephemeral) public key is on the curve of
041         * the private key. Intended to prevent an "Invalid Curve Attack",
042         * independent from any JCA provider checks (the SUN provider in Java
043         * 1.8.0_51+ and BouncyCastle have them, other / older provider do
044         * not).
045         *
046         * <p>See https://www.cs.bris.ac.uk/Research/CryptographySecurity/RWC/2017/nguyen.quan.pdf
047         *
048         * @param publicKey  The public EC key. Must not be {@code null}.
049         * @param privateKey The private EC key. Must not be {@code null}.
050         *
051         * @return {@code true} if public key passed the curve check.
052         */
053        public static boolean isPointOnCurve(final ECPublicKey publicKey, final ECPrivateKey privateKey) {
054                
055                return isPointOnCurve(publicKey, privateKey.getParams());
056        }
057        
058        
059        /**
060         * Checks if the specified (ephemeral) public key is on the given
061         * curve. Intended to prevent an "Invalid Curve Attack", independent
062         * from any JCA provider checks (the SUN provider in Java 1.8.0_51+ and
063         * BouncyCastle have them, other / older provider do not).
064         *
065         * <p>See https://www.cs.bris.ac.uk/Research/CryptographySecurity/RWC/2017/nguyen.quan.pdf
066         *
067         * @param publicKey       The public EC key. Must not be {@code null}.
068         * @param ecParameterSpec The EC spec. Must not be {@code null}.
069         *
070         * @return {@code true} if public key passed the curve check.
071         */
072        public static boolean isPointOnCurve(final ECPublicKey publicKey, final ECParameterSpec ecParameterSpec) {
073                
074                ECPoint point = publicKey.getW();
075                return isPointOnCurve(point.getAffineX(), point.getAffineY(), ecParameterSpec);
076        }
077        
078        
079        /**
080         * Checks if the specified (ephemeral) public key is on the given
081         * curve. Intended to prevent an "Invalid Curve Attack", independent
082         * from any JCA provider checks (the SUN provider in Java 1.8.0_51+ and
083         * BouncyCastle have them, other / older provider do not).
084         *
085         * <p>See https://www.cs.bris.ac.uk/Research/CryptographySecurity/RWC/2017/nguyen.quan.pdf
086         *
087         * @param x               The public EC x coordinate. Must not be
088         *                        {@code null}.
089         * @param y               The public EC y coordinate. Must not be
090         *                        {@code null}.
091         * @param ecParameterSpec The EC spec. Must not be {@code null}.
092         *
093         * @return {@code true} if public key passed the curve check.
094         */
095        public static boolean isPointOnCurve(final BigInteger x, final BigInteger y, final ECParameterSpec ecParameterSpec) {
096                
097                // Ensure the following condition is met:
098                // (y^2) mod p = (x^3 + ax + b) mod p
099                EllipticCurve curve = ecParameterSpec.getCurve();
100                BigInteger a = curve.getA();
101                BigInteger b = curve.getB();
102                BigInteger p = ((ECFieldFp) curve.getField()).getP();
103                BigInteger leftSide = (y.pow(2)).mod(p);
104                BigInteger rightSide = (x.pow(3).add(a.multiply(x)).add(b)).mod(p);
105                
106                return leftSide.equals(rightSide);
107        }
108        
109        
110        /**
111         * Prevents public instantiation.
112         */
113        private ECChecks() {}
114}