001/*
002 * nimbus-jose-jwt
003 *
004 * Copyright 2012-2018, Connect2id Ltd.
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;
019
020
021import java.security.GeneralSecurityException;
022
023import com.google.crypto.tink.subtle.Ed25519Sign;
024import com.nimbusds.jose.JOSEException;
025import com.nimbusds.jose.JWSAlgorithm;
026import com.nimbusds.jose.JWSHeader;
027import com.nimbusds.jose.JWSSigner;
028import com.nimbusds.jose.jwk.Curve;
029import com.nimbusds.jose.jwk.OctetKeyPair;
030import com.nimbusds.jose.util.Base64URL;
031import net.jcip.annotations.ThreadSafe;
032
033
034/**
035 * Ed25519 signer of {@link com.nimbusds.jose.JWSObject JWS objects}.
036 * Expects an {@link OctetKeyPair} with {@code "crv"} Ed25519.
037 * Uses the Edwards-curve Digital Signature Algorithm (EdDSA).
038 *
039 * <p>See <a href="https://tools.ietf.org/html/rfc8037">RFC 8037</a>
040 * for more information.
041 *
042 * <p>This class is thread-safe.
043 *
044 * <p>Supports the following algorithm:
045 *
046 * <ul>
047 *     <li>{@link com.nimbusds.jose.JWSAlgorithm#EdDSA}
048 * </ul>
049 *
050 * <p>with the following curve:
051 *
052 * <ul>
053 *     <li>{@link com.nimbusds.jose.jwk.Curve#Ed25519}
054 * </ul>
055 *
056 * @author Tim McLean
057 * @version 2018-07-11
058 */
059@ThreadSafe
060public class Ed25519Signer extends EdDSAProvider implements JWSSigner {
061        
062        
063        private final OctetKeyPair privateKey;
064
065
066        private final Ed25519Sign tinkSigner;
067
068
069        /**
070         * Creates a new Ed25519 signer.
071         *
072         * @param privateKey The private key. Must be non-{@code null}, and must
073         * be of type Ed25519 ({@code "crv": "Ed25519"}).
074         *
075         * @throws JOSEException If the key subtype is not supported or if the key is not a private key
076         */
077        public Ed25519Signer(final OctetKeyPair privateKey)
078                throws JOSEException {
079
080                super();
081
082                if (! Curve.Ed25519.equals(privateKey.getCurve())) {
083                        throw new JOSEException("Ed25519Signer only supports OctetKeyPairs with crv=Ed25519");
084                }
085
086                if (! privateKey.isPrivate()) {
087                        throw new JOSEException("The OctetKeyPair doesn't contain a private part");
088                }
089
090                this.privateKey = privateKey;
091
092                try {
093                        tinkSigner = new Ed25519Sign(privateKey.getDecodedD());
094
095                } catch (GeneralSecurityException e) {
096                        // If Tink failed to initialize; generally should not happen
097                        throw new JOSEException(e.getMessage(), e);
098                }
099        }
100
101
102        /**
103         * Gets the Ed25519 private key as an {@code OctetKeyPair}.
104         *
105         * @return The private key.
106         */
107        public OctetKeyPair getPrivateKey() {
108                
109                return privateKey;
110        }
111
112
113        @Override
114        public Base64URL sign(final JWSHeader header, final byte[] signingInput)
115                throws JOSEException {
116
117                // Check alg field in header
118                final JWSAlgorithm alg = header.getAlgorithm();
119                if (! JWSAlgorithm.EdDSA.equals(alg)) {
120                        throw new JOSEException("Ed25519Signer requires alg=EdDSA in JWSHeader");
121                }
122
123                final byte[] jwsSignature;
124
125                try {
126                        jwsSignature = tinkSigner.sign(signingInput);
127
128                } catch (GeneralSecurityException e) {
129
130                        throw new JOSEException(e.getMessage(), e);
131                }
132
133                return Base64URL.encode(jwsSignature);
134        }
135}