seanjmullan commented on code in PR #234: URL: https://github.com/apache/santuario-xml-security-java/pull/234#discussion_r1431775920
########## src/main/java/org/apache/xml/security/encryption/keys/content/AgreementMethodImpl.java: ########## @@ -0,0 +1,325 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.xml.security.encryption.keys.content; + +import org.apache.xml.security.encryption.AgreementMethod; +import org.apache.xml.security.encryption.KeyDerivationMethod; +import org.apache.xml.security.encryption.XMLEncryptionException; +import org.apache.xml.security.encryption.params.ConcatKDFParams; +import org.apache.xml.security.encryption.params.KeyAgreementParameters; +import org.apache.xml.security.exceptions.XMLSecurityException; +import org.apache.xml.security.encryption.keys.OriginatorKeyInfo; +import org.apache.xml.security.encryption.keys.RecipientKeyInfo; +import org.apache.xml.security.keys.content.KeyInfoContent; +import org.apache.xml.security.encryption.keys.content.derivedKey.ConcatKDFParamsImpl; +import org.apache.xml.security.encryption.keys.content.derivedKey.KeyDerivationMethodImpl; +import org.apache.xml.security.utils.*; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import java.lang.System.Logger; +import java.lang.System.Logger.Level; +import java.net.URI; +import java.net.URISyntaxException; +import java.security.PublicKey; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + + +/** + * The implementation of the AgreementMethod interface. The element contains a information about + * the key agreement algorithm for deriving the encryption key. + * + */ +public class AgreementMethodImpl extends EncryptionElementProxy implements KeyInfoContent, AgreementMethod { + protected static final Logger LOG = System.getLogger(AgreementMethodImpl.class.getName()); + + private byte[] kaNonce; + private List<Element> agreementMethodInformation; + private KeyDerivationMethod keyDerivationMethod; + private OriginatorKeyInfo originatorKeyInfo; + private RecipientKeyInfo recipientKeyInfo; + private String algorithmURI; + + /** + * Constructor AgreementMethodImpl for generating AgreementMethod from scratch based on {@link KeyAgreementParameters}. + * The constructor generates {@link KeyDerivationMethod} if given and {@link OriginatorKeyInfo} based on originator + * public key for ECDH-ES key agreement. It generates a placeholder element for RecipientKeyInfo. The recipient key info value + * must be set later. + * + * @param doc the {@link Document} in which <code>AgreementMethod</code> will be placed + * @param keyAgreementParameter the {@link KeyAgreementParameters} from which <code>AgreementMethod</code> will be generated + * @throws XMLEncryptionException + */ + public AgreementMethodImpl(Document doc, KeyAgreementParameters keyAgreementParameter) throws XMLEncryptionException { + this(doc, keyAgreementParameter.getKeyAgreementAlgorithm()); + + if (keyAgreementParameter.getKeyDerivationParameter() != null) { + KeyDerivationMethod keyDerivationMethod = createKeyDerivationMethod(keyAgreementParameter); + setKeyDerivationMethod(keyDerivationMethod); + } + // if ephemeral static key agreement then add originator public key automatically + if (EncryptionConstants.ALGO_ID_KEYAGREEMENT_ECDH_ES.equals(keyAgreementParameter.getKeyAgreementAlgorithm())) { + setOriginatorPublicKey(keyAgreementParameter.getOriginatorPublicKey()); + } + // set recipient key info holder + RecipientKeyInfo recipientKeyInfo = new RecipientKeyInfo(getDocument()); + setRecipientKeyInfo(recipientKeyInfo); + } + + /** + * Constructor AgreementMethodImpl for generating AgreementMethod from scratch based on algorithm URI. The constructor + * builds a placeholder element for {@link KeyDerivationMethod}, {@link OriginatorKeyInfo} and {@link RecipientKeyInfo}. The values for these elements + * must be set later. + * + * @param algorithm the algorithm URI for the key agreement algorithm + */ + public AgreementMethodImpl(Document doc, String algorithm) { + super(doc); + + agreementMethodInformation = new LinkedList<>(); + URI tmpAlgorithm; + try { + tmpAlgorithm = new URI(algorithm); + } catch (URISyntaxException ex) { + throw new IllegalArgumentException("Algorithm [" + algorithm + "] is not URI ", ex); + } + algorithmURI = tmpAlgorithm.toString(); + + setLocalAttribute(Constants._ATT_ALGORITHM, algorithmURI); + } + + /** + * Constructor AgreementMethodImpl based on XML {@link Element}. + * + * @param element the XML {@link Element} containing AgreementMethod information + * @throws XMLSecurityException + */ + public AgreementMethodImpl(Element element) throws XMLSecurityException { + super(element, EncryptionConstants.EncryptionSpecNS); + } + + /** + * {@inheritDoc} + */ + @Override + public byte[] getKANonce() { + return kaNonce; + } + + /** + * {@inheritDoc} + */ + @Override + public void setKANonce(byte[] kanonce) { + kaNonce = kanonce; + } + + /** + * {@inheritDoc} + */ + @Override + public Iterator<Element> getAgreementMethodInformation() { + return agreementMethodInformation.iterator(); + } + + /** + * {@inheritDoc} + */ + @Override + public void addAgreementMethodInformation(Element info) { + agreementMethodInformation.add(info); + } + + /** + * {@inheritDoc} + */ + @Override + public void removeAgreementMethodInformation(Element info) { + agreementMethodInformation.remove(info); + } + + /** + * {@inheritDoc} + */ + @Override + public KeyDerivationMethod getKeyDerivationMethod() throws XMLSecurityException { + + if (keyDerivationMethod != null) { + return keyDerivationMethod; + } + + Element keyDerivationMethodElement = + XMLUtils.selectXenc11Node(getElement().getFirstChild(), EncryptionConstants._TAG_KEYDERIVATIONMETHOD, 0); + + if (keyDerivationMethodElement == null) { + return null; + } + keyDerivationMethod = new KeyDerivationMethodImpl(keyDerivationMethodElement, baseURI); + + + return keyDerivationMethod; + } + + /** + * {@inheritDoc} + */ + @Override + public void setKeyDerivationMethod(KeyDerivationMethod keyDerivationMethod) { + this.keyDerivationMethod = keyDerivationMethod; + if (keyDerivationMethod instanceof ElementProxy) { + appendSelf((ElementProxy) keyDerivationMethod); + addReturnToSelf(); + } else { + LOG.log(Level.WARNING, "KeyDerivationMethod [{0}] is set but is not an instance of ElementProxy. " + + "The DOM node is lost upon serialization.", keyDerivationMethod); + } + } + + /** + * {@inheritDoc} + */ + @Override + public OriginatorKeyInfo getOriginatorKeyInfo() throws XMLSecurityException { + if (originatorKeyInfo != null) { + return originatorKeyInfo; + } + + Element originatorKeyInfoElement = + XMLUtils.selectXencNode(getElement().getFirstChild(), EncryptionConstants._TAG_ORIGINATORKEYINFO, 0); + + if (originatorKeyInfoElement == null) { + return null; + } + originatorKeyInfo = new OriginatorKeyInfo(originatorKeyInfoElement, baseURI); + + return originatorKeyInfo; + } + + /** + * {@inheritDoc} + */ + @Override + public void setOriginatorKeyInfo(OriginatorKeyInfo keyInfo) { + originatorKeyInfo = keyInfo; + appendSelf(keyInfo); + addReturnToSelf(); + } + + /** + * {@inheritDoc} + */ + @Override + public void setOriginatorPublicKey(PublicKey publicKey) { + + OriginatorKeyInfo originatorKeyInfo = new OriginatorKeyInfo(getDocument()); + originatorKeyInfo.add(publicKey); + setOriginatorKeyInfo(originatorKeyInfo); + } + + /** + * {@inheritDoc} + */ + @Override + public RecipientKeyInfo getRecipientKeyInfo() throws XMLSecurityException { + if (recipientKeyInfo != null) { + return recipientKeyInfo; + } + + Element recipientKeyInfoElement = + XMLUtils.selectXencNode(getElement().getFirstChild(), EncryptionConstants._TAG_RECIPIENTKEYINFO, 0); + + if (recipientKeyInfoElement == null) { + return null; + } + recipientKeyInfo = new RecipientKeyInfo(recipientKeyInfoElement, baseURI); + + return recipientKeyInfo; + } + + /** + * {@inheritDoc} + */ + @Override + public void setRecipientKeyInfo(RecipientKeyInfo keyInfo) { + + recipientKeyInfo = keyInfo; + appendSelf(keyInfo); + addReturnToSelf(); + } + + /** + * {@inheritDoc} + */ + @Override + public String getAlgorithm() { + if (algorithmURI == null) { + algorithmURI = getLocalAttribute(Constants._ATT_ALGORITHM); + } + return algorithmURI; + } + + @Override + public String getBaseLocalName() { + return EncryptionConstants._TAG_AGREEMENTMETHOD; + } + + /** + * Method createKeyDerivationMethod creates a {@link KeyDerivationMethod} based on {@link KeyAgreementParameters}. + * The method supports only {@link ConcatKDFParams} for now. + * @see <a href="https://www.w3.org/TR/xmlenc-core1/#sec-ConcatKDF">ConcatKDF</a> + * + * @param keyAgreementParameter the {@link KeyAgreementParameters} from which {@link KeyDerivationMethod} will be generated. + * @return the {@link KeyDerivationMethod} based on {@link KeyAgreementParameters} + * @throws XMLEncryptionException if Key derivation algorithm is not supported + */ + private KeyDerivationMethod createKeyDerivationMethod(KeyAgreementParameters keyAgreementParameter) throws XMLEncryptionException { + ConcatKDFParams kdfParameters = (ConcatKDFParams) Review Comment: Maybe you should throw an exception if parameters are not of the expected type. ########## src/main/java/org/apache/xml/security/encryption/keys/content/derivedKey/ConcatKDF.java: ########## @@ -0,0 +1,240 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.xml.security.encryption.keys.content.derivedKey; + +import org.apache.xml.security.algorithms.MessageDigestAlgorithm; +import org.apache.xml.security.encryption.XMLEncryptionException; +import org.apache.xml.security.exceptions.XMLSecurityException; + +import java.lang.System.Logger.Level; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.security.MessageDigest; +import java.util.ArrayList; +import java.util.List; + + +/** + * Key DerivationAlgorithm implementation, defined in Section 5.8.1 of NIST SP 800-56A [SP800-56A], and is equivalent + * to the KDF3 function defined in ANSI X9.44-2007 [ANSI-X9-44-2007] when the contents of the OtherInfo parameter + * is structured as in NIST SP 800-56A. + * <p> + * Identifier: http://www.w3.org/2009/xmlenc11#ConcatKDF + */ +public class ConcatKDF implements DerivationAlgorithm { + + private static final System.Logger LOG = System.getLogger(ConcatKDF.class.getName()); + + private final String algorithmURI; + + /** + * Constructor ConcatKDF with digest algorithmURI parameter such as http://www.w3.org/2001/04/xmlenc#sha256, + * http://www.w3.org/2001/04/xmlenc#sha512, etc. + */ + public ConcatKDF(String algorithmURI) { + this.algorithmURI = algorithmURI; + } + + + /** + * Default Constructor which sets the default digest algorithmURI parameter: http://www.w3.org/2001/04/xmlenc#sha256, + */ + public ConcatKDF() { + this(MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA256); + } + + + + /** + * Key DerivationAlgorithm implementation as defined in Section 5.8.1 of NIST SP 800-56A [SP800-56A] + * <ul> + * <li> reps = ⎡ keydatalen / hashlen⎤.</li> + * <li> If reps > (2>32 −1), then ABORT: output an error indicator and stop.</li> + * <li> Initialize a 32-bit, big-endian bit string counter as 0000000116.</li> + * <li> If counter || Z || OtherInfo is more than max_hash_inputlen bits long, then ABORT: output an error indicator and stop. + * <li> For i = 1 to reps by 1, do the following:<ul> + * <li> Compute Hashi = H(counter || Z || OtherInfo).</li> + * <li> Increment counter (modulo 232), treating it as an unsigned 32-bit integer.</li> + * </ul></li> + * <li> Let Hhash be set to Hashreps if (keydatalen / hashlen) is an integer; otherwise, let Hhash be set to the + * (keydatalen mod hashlen) leftmost bits of Hashreps.</li> + * <li>Set DerivedKeyingMaterial = Hash1 || Hash2 || ... || Hashreps-1 || Hhash</li> + * </ul> + * + * @param secret The "shared" secret to use for key derivation (e.g. the secret key) + * @param otherInfo as specified in [SP800-56A] the optional attributes: AlgorithmID, PartyUInfo, PartyVInfo, SuppPubInfo and SuppPrivInfo attributes are concatenated to form a bit string “OtherInfo” that is used with the key derivation function. + * @param offset the offset parameter is ignored by this implementation. + * @param keyLength The length of the key to derive + * @return The derived key + * @throws XMLEncryptionException if the key length is too long to be derived with the given algorithm + */ + @Override + public byte[] deriveKey(byte[] secret, byte[] otherInfo, int offset, long keyLength) throws XMLSecurityException { + + MessageDigest digest = MessageDigestAlgorithm.getDigestInstance(algorithmURI); + + long genKeyLength = offset+keyLength; + + int iDigestLength = digest.getDigestLength(); + if (genKeyLength / iDigestLength > (long) Integer.MAX_VALUE) { + LOG.log(Level.ERROR, "Key size is to long to be derived with hash algorithm [{0}]", algorithmURI); + throw new XMLEncryptionException("errorInKeyDerivation"); + } + int toGenerateSize = (int) genKeyLength; + + digest.reset(); + ByteBuffer indexBuffer = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN); + + ByteBuffer result = ByteBuffer.allocate(toGenerateSize); + + int counter = 1; + while (toGenerateSize > 0) { + indexBuffer.position(0); + indexBuffer.putInt(counter++); + indexBuffer.position(0); + digest.update(indexBuffer); + digest.update(secret); + if (otherInfo != null && otherInfo.length > 0) { + digest.update(otherInfo); + } + result.put(digest.digest(), 0, Math.min(toGenerateSize, iDigestLength)); + toGenerateSize -= iDigestLength; + } + if (offset > 0) { + result.position(offset); + return result.slice().array(); + } + return result.array(); + } + + /** + * Method concatenate the bitstrings in following order {@code algID || partyUInfo || partyVInfo || suppPubInfo || suppPrivInfo}. + * to crate otherInfo as key derivation function input. + * If named parameters are null the value is ignored. + * Method parses the bitstring firs {{@code @See} https://www.w3.org/TR/xmlenc-core1/#sec-ConcatKDF} and then concatenates them to a byte array. + * + * @param sharedSecret The "shared" secret to use for key derivation (e.g. the secret key) + * @param algID A bit string that indicates how the derived keying material will be parsed and for which + * algorithm(s) the derived secret keying material will be used. + * @param partyUInfo A bit string containing public information that is required by the + * application using this KDF to be contributed by party U to the key derivation + * process. At a minimum, PartyUInfo shall include IDU, the identifier of party U. See + * the notes below.. + * @param partyVInfo A bit string containing public information that is required by the + * application using this KDF to be contributed by party V to the key derivation + * process. At a minimum, PartyVInfo shall include IDV, the identifier of party V. See + * the notes below. + * @param suppPubInfo bit string containing additional, mutually-known public information. + * @param suppPrivInfo The suppPrivInfo A bit string containing additional, mutually-known public Information. + * @param keyLength The length of the key to derive + * @return The resulting other info. + */ + public byte[] deriveKey(final byte[] sharedSecret, + final String algID, + final String partyUInfo, + final String partyVInfo, + final String suppPubInfo, + final String suppPrivInfo, + final long keyLength) + throws XMLSecurityException { + + final byte[] otherInfo = concatParameters(algID, partyUInfo, partyVInfo, suppPubInfo, suppPrivInfo); + + return deriveKey(sharedSecret, otherInfo, keyLength); + } + + + /** + * Simple method to concatenate non-padded bitstream ConcatKDF parameters. + * If parameters are null the value is ignored. + * + * @param parameters the parameters to concatenate + * @return the concatenated parameters as byte array + */ + public static byte[] concatParameters(final String... parameters) throws XMLEncryptionException { Review Comment: Maybe this doesn't need to be public since it is only called by this class AFAICT. ########## src/main/java/org/apache/xml/security/encryption/keys/content/derivedKey/DerivationAlgorithm.java: ########## @@ -0,0 +1,57 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.xml.security.encryption.keys.content.derivedKey; + +import org.apache.xml.security.encryption.XMLEncryptionException; +import org.apache.xml.security.exceptions.XMLSecurityException; + +/** + * Interface is supported by classes to implement key derivation algorithms. + */ +public interface DerivationAlgorithm { + + /** + * Derives a key from the given secret and other info. The initial derived key is size of + * offset + keyLength. + * + * + * @param secret The "shared" secret to use for key derivation (e.g. the secret key) + * @param otherInfo as specified in [SP800-56A] the optional attributes: AlgorithmID, PartyUInfo, PartyVInfo, SuppPubInfo and SuppPrivInfo attributes are concatenated to form a bit string “OtherInfo” that is used with the key derivation function. + * @param offset the starting position in derived keying material of size: offset + keyLength + * @param keyLength The length of the key to derive + * @return The derived key + * @throws XMLEncryptionException if something goes wrong during the key derivation Review Comment: s/XMLEncryptionException/XMLSecurityException/ -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
