code: /*
   * Copyright (c) 2019 the Eclipse Milo Authors
   *
   * This program and the accompanying materials are made
   * available under the terms of the Eclipse Public License 2.0
   * which is available at https://www.eclipse.org/legal/epl-2.0/
   *
   * SPDX-License-Identifier: EPL-2.0
   */
  
  package org.eclipse.milo.opcua.stack.core.channel;
  
  import java.security.KeyPair;
  import java.security.PublicKey;
  import java.security.cert.Certificate;
  import java.security.cert.CertificateEncodingException;
  import java.security.cert.X509Certificate;
  import java.security.interfaces.RSAPublicKey;
  import java.util.List;
  
  import org.eclipse.milo.opcua.stack.core.StatusCodes;
  import org.eclipse.milo.opcua.stack.core.UaException;
  import org.eclipse.milo.opcua.stack.core.channel.ChannelSecurity.SecretKeys;
  import org.eclipse.milo.opcua.stack.core.channel.ChannelSecurity.SecurityKeys;
  import org.eclipse.milo.opcua.stack.core.security.SecurityAlgorithm;
  import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy;
  import org.eclipse.milo.opcua.stack.core.types.builtin.ByteString;
  import org.eclipse.milo.opcua.stack.core.types.enumerated.MessageSecurityMode;
  import org.eclipse.milo.opcua.stack.core.util.DigestUtil;
  
  import static org.eclipse.milo.opcua.stack.core.util.CertificateUtil.getCertificateChainBytes;
  
  public interface SecureChannel {
  
      KeyPair getKeyPair();
  
      X509Certificate getLocalCertificate();
  
      List<X509Certificate> getLocalCertificateChain();
  
      X509Certificate getRemoteCertificate();
  
      List<X509Certificate> getRemoteCertificateChain();
  
      SecurityPolicy getSecurityPolicy();
  
      MessageSecurityMode getMessageSecurityMode();
  
      long getChannelId();
  
      ChannelSecurity getChannelSecurity();
  
      SecretKeys getEncryptionKeys(SecurityKeys securityKeys);
  
      SecretKeys getDecryptionKeys(SecurityKeys securityKeys);
  
      ByteString getLocalNonce();
  
      ByteString getRemoteNonce();
  
      default ByteString getLocalCertificateBytes() throws UaException {
          try {
              return getLocalCertificate() != null ?
                  ByteString.of(getLocalCertificate().getEncoded()) :
                  ByteString.NULL_VALUE;
          } catch (CertificateEncodingException e) {
              throw new UaException(StatusCodes.Bad_CertificateInvalid, e);
          }
      }
  
      default ByteString getLocalCertificateChainBytes() throws UaException {
          List<X509Certificate> localCertificateChain = getLocalCertificateChain();
  
          if (localCertificateChain != null) {
              return getCertificateChainBytes(localCertificateChain);
          } else {
              return ByteString.NULL_VALUE;
          }
      }
  
      default ByteString getLocalCertificateThumbprint() throws UaException {
          try {
              return getLocalCertificate() != null ?
                  ByteString.of(DigestUtil.sha1(getLocalCertificate().getEncoded())) :
                  ByteString.NULL_VALUE;
          } catch (CertificateEncodingException e) {
              throw new UaException(StatusCodes.Bad_CertificateInvalid, e);
          }
      }
  
  
      default ByteString getRemoteCertificateBytes() throws UaException {
          try {
              return getRemoteCertificate() != null ?
                  ByteString.of(getRemoteCertificate().getEncoded()) :
                  ByteString.NULL_VALUE;
          } catch (CertificateEncodingException e) {
              throw new UaException(StatusCodes.Bad_CertificateInvalid, e);
          }
      }
  
      default ByteString getRemoteCertificateChainBytes() throws UaException {
          List<X509Certificate> remoteCertificateChain = getRemoteCertificateChain();
  
          if (remoteCertificateChain != null) {
              return getCertificateChainBytes(remoteCertificateChain);
          } else {
              return ByteString.NULL_VALUE;
          }
      }
  
      default ByteString getRemoteCertificateThumbprint() throws UaException {
          try {
              return getRemoteCertificate() != null ?
                  ByteString.of(DigestUtil.sha1(getRemoteCertificate().getEncoded())) :
                  ByteString.NULL_VALUE;
          } catch (CertificateEncodingException e) {
              throw new UaException(StatusCodes.Bad_CertificateInvalid, e);
          }
      }
  
      default int getLocalAsymmetricCipherTextBlockSize() {
          if (isAsymmetricEncryptionEnabled()) {
              SecurityAlgorithm algorithm = getSecurityPolicy().getAsymmetricEncryptionAlgorithm();
  
              return getAsymmetricCipherTextBlockSize(getLocalCertificate(), algorithm);
          } else {
              return 1;
          }
      }
  
      default int getRemoteAsymmetricCipherTextBlockSize() {
          if (isAsymmetricEncryptionEnabled()) {
              SecurityAlgorithm algorithm = getSecurityPolicy().getAsymmetricEncryptionAlgorithm();
  
              return getAsymmetricCipherTextBlockSize(getRemoteCertificate(), algorithm);
          } else {
              return 1;
          }
      }
  
      default int getLocalAsymmetricPlainTextBlockSize() {
          if (isAsymmetricEncryptionEnabled()) {
              SecurityAlgorithm algorithm = getSecurityPolicy().getAsymmetricEncryptionAlgorithm();
  
              return getAsymmetricPlainTextBlockSize(getLocalCertificate(), algorithm);
          } else {
              return 1;
          }
      }
  
      default int getRemoteAsymmetricPlainTextBlockSize() {
          if (isAsymmetricEncryptionEnabled()) {
              SecurityAlgorithm algorithm = getSecurityPolicy().getAsymmetricEncryptionAlgorithm();
  
              return getAsymmetricPlainTextBlockSize(getRemoteCertificate(), algorithm);
          } else {
              return 1;
          }
      }
  
      default int getLocalAsymmetricSignatureSize() {
          SecurityAlgorithm algorithm = getSecurityPolicy().getAsymmetricSignatureAlgorithm();
  
          return getAsymmetricSignatureSize(getLocalCertificate(), algorithm);
      }
  
      default int getRemoteAsymmetricSignatureSize() {
          SecurityAlgorithm algorithm = getSecurityPolicy().getAsymmetricSignatureAlgorithm();
  
          return getAsymmetricSignatureSize(getRemoteCertificate(), algorithm);
      }
  
      default boolean isAsymmetricSigningEnabled() {
          return getSecurityPolicy() != SecurityPolicy.None &&
              getLocalCertificate() != null;
      }
  
      default boolean isAsymmetricEncryptionEnabled() {
          return getSecurityPolicy() != SecurityPolicy.None &&
              getLocalCertificate() != null &&
              getRemoteCertificate() != null;
      }
  
      default int getSymmetricBlockSize() {
          if (isSymmetricEncryptionEnabled()) {
              SecurityAlgorithm algorithm = getSecurityPolicy().getSymmetricEncryptionAlgorithm();
  
              switch (algorithm) {
                  case Aes128:
                  case Aes256:
                      return 16;
                  default:
                      return 1;
              }
          } else {
              return 1;
          }
      }
  
      default int getSymmetricSignatureSize() {
          SecurityAlgorithm algorithm = getSecurityPolicy().getSymmetricSignatureAlgorithm();
  
          switch (algorithm) {
              case HmacSha1:
                  return 20;
              case HmacSha256:
                  return 32;
              default:
                  return 0;
          }
      }
  
      default int getSymmetricSignatureKeySize() {
          switch (getSecurityPolicy()) {
              case None:
                  return 0;
              case Basic128Rsa15:
                  return 16;
              case Basic256:
                  return 24;
              case Basic256Sha256:
              case Aes128_Sha256_RsaOaep:
              case Aes256_Sha256_RsaPss:
                  return 32;
              default:
                  return 0;
          }
      }
  
      default int getSymmetricEncryptionKeySize() {
          switch (getSecurityPolicy()) {
              case None:
                  return 0;
              case Basic128Rsa15:
              case Aes128_Sha256_RsaOaep:
                  return 16;
              case Basic256:
              case Basic256Sha256:
              case Aes256_Sha256_RsaPss:
                  return 32;
              default:
                  return 0;
          }
      }
  
      default boolean isSymmetricSigningEnabled() {
          return getLocalCertificate() != null &&
              getSecurityPolicy() != SecurityPolicy.None &&
              (getMessageSecurityMode() == MessageSecurityMode.Sign ||
                  getMessageSecurityMode() == MessageSecurityMode.SignAndEncrypt);
      }
  
      default boolean isSymmetricEncryptionEnabled() {
          return getRemoteCertificate() != null &&
              getSecurityPolicy() != SecurityPolicy.None &&
              getMessageSecurityMode() == MessageSecurityMode.SignAndEncrypt;
      }
  
      static int getAsymmetricKeyLength(Certificate certificate) {
          PublicKey publicKey = certificate != null ?
              certificate.getPublicKey() : null;
  
          return (publicKey instanceof RSAPublicKey) ?
              ((RSAPublicKey) publicKey).getModulus().bitLength() : 0;
      }
  
      static int getAsymmetricSignatureSize(Certificate certificate, SecurityAlgorithm algorithm) {
          switch (algorithm) {
              case RsaSha1:
              case RsaSha256:
              case RsaSha256Pss:
                  return (getAsymmetricKeyLength(certificate) + 7) / 8;
              default:
                  return 0;
          }
      }
  
      static int getAsymmetricCipherTextBlockSize(Certificate certificate, SecurityAlgorithm algorithm) {
          switch (algorithm) {
              case Rsa15:
              case RsaOaepSha1:
              case RsaOaepSha256:
                  return (getAsymmetricKeyLength(certificate) + 7) / 8;
              default:
                  return 1;
          }
      }
  
      static int getAsymmetricPlainTextBlockSize(X509Certificate certificate, SecurityAlgorithm algorithm) {
          switch (algorithm) {
              case Rsa15:
                  return (getAsymmetricKeyLength(certificate) + 7) / 8 - 11;
              case RsaOaepSha1:
                  return (getAsymmetricKeyLength(certificate) + 7) / 8 - 42;
              case RsaOaepSha256:
                  return (getAsymmetricKeyLength(certificate) + 7) / 8 - 66;
              default:
                  return 1;
          }
      }
  
  }
  