SignedCertificateTimestamp.java

/*
 * Copyright 2022 The Sigstore Authors.
 * Copyright 2015 The Android Open Source Project.
 *
 * Licensed 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
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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 dev.sigstore.encryption.certificates.transparency;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

/** SignedCertificateTimestamp structure, as defined by RFC6962 Section 3.2. */
public class SignedCertificateTimestamp {
  public enum Version {
    V1
  };

  public enum SignatureType {
    CERTIFICATE_TIMESTAMP,
    TREE_HASH
  };

  public enum Origin {
    EMBEDDED,
    TLS_EXTENSION,
    OCSP_RESPONSE
  };

  private final Version version;
  private final byte[] logId;
  private final long timestamp;
  private final byte[] extensions;
  private final DigitallySigned signature;

  // origin is implied from the SCT's source and is not encoded in it,
  // and affects the verification process.
  private final Origin origin;

  public SignedCertificateTimestamp(
      Version version,
      byte[] logId,
      long timestamp,
      byte[] extensions,
      DigitallySigned signature,
      Origin origin) {
    this.version = version;
    this.logId = logId;
    this.timestamp = timestamp;
    this.extensions = extensions;
    this.signature = signature;
    this.origin = origin;
  }

  public Version getVersion() {
    return version;
  }

  public byte[] getLogID() {
    return logId;
  }

  public long getTimestamp() {
    return timestamp;
  }

  public byte[] getExtensions() {
    return extensions;
  }

  public DigitallySigned getSignature() {
    return signature;
  }

  public Origin getOrigin() {
    return origin;
  }

  /** Decode a TLS encoded SignedCertificateTimestamp structure. */
  @SuppressWarnings("EnumOrdinal")
  public static SignedCertificateTimestamp decode(InputStream input, Origin origin)
      throws SerializationException {
    int version = Serialization.readNumber(input, CTConstants.VERSION_LENGTH);
    if (version != Version.V1.ordinal()) {
      throw new SerializationException("Unsupported SCT version " + version);
    }

    return new SignedCertificateTimestamp(
        Version.V1,
        Serialization.readFixedBytes(input, CTConstants.LOGID_LENGTH),
        Serialization.readLong(input, CTConstants.TIMESTAMP_LENGTH),
        Serialization.readVariableBytes(input, CTConstants.EXTENSIONS_LENGTH_BYTES),
        DigitallySigned.decode(input),
        origin);
  }

  /** Decode a TLS encoded SignedCertificateTimestamp structure. */
  public static SignedCertificateTimestamp decode(byte[] input, Origin origin)
      throws SerializationException {
    return decode(new ByteArrayInputStream(input), origin);
  }

  /** TLS encode the signed part of the SCT, as described by RFC6962 section 3.2. */
  @SuppressWarnings("EnumOrdinal")
  public void encodeTBS(OutputStream output, CertificateEntry certEntry)
      throws SerializationException {
    Serialization.writeNumber(output, version.ordinal(), CTConstants.VERSION_LENGTH);
    Serialization.writeNumber(
        output, SignatureType.CERTIFICATE_TIMESTAMP.ordinal(), CTConstants.SIGNATURE_TYPE_LENGTH);
    Serialization.writeNumber(output, timestamp, CTConstants.TIMESTAMP_LENGTH);
    certEntry.encode(output);
    Serialization.writeVariableBytes(output, extensions, CTConstants.EXTENSIONS_LENGTH_BYTES);
  }

  /** TLS encode the signed part of the SCT, as described by RFC6962 section 3.2. */
  public byte[] encodeTBS(CertificateEntry certEntry) throws SerializationException {
    ByteArrayOutputStream output = new ByteArrayOutputStream();
    encodeTBS(output, certEntry);
    return output.toByteArray();
  }
}