Skip to content

Commit

Permalink
Add a kotlin format linter
Browse files Browse the repository at this point in the history
Includes the fixes applied by the fool.

Change-Id: If09d8828ae73efeb1b8fc51b2ef57222816e7b7f
  • Loading branch information
jblebrun committed Jun 27, 2024
1 parent 77953fc commit 6a40d36
Show file tree
Hide file tree
Showing 14 changed files with 132 additions and 73 deletions.
2 changes: 2 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@
cargo-deadlinks
clang-tools
hadolint
ktfmt
ktlint
nixpkgs-fmt
nodePackages.markdownlint-cli
shellcheck
Expand Down
14 changes: 6 additions & 8 deletions java/src/main/java/com/google/oak/crypto/DecryptionResult.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,12 @@
// limitations under the License.
//

package com.google.oak.crypto;
package com.google.oak.crypto

class DecryptionResult(
// This is needed because the Java code reference backing fields directly.
// By default, Kotlin properties generate getter/setter methods, and does not
// expose the backing fields directly.
@JvmField
val plaintext: ByteArray,
@JvmField
val associatedData: ByteArray,
// This is needed because the Java code reference backing fields directly.
// By default, Kotlin properties generate getter/setter methods, and does not
// expose the backing fields directly.
@JvmField val plaintext: ByteArray,
@JvmField val associatedData: ByteArray,
)
23 changes: 13 additions & 10 deletions java/src/main/java/com/google/oak/verification/LogEntryVerifier.kt
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@ object LogEntryVerifier {
* @return empty if the verification succeeds, or a failure otherwise
*/
fun verify(
logEntry: RekorLogEntry,
publicKeyBytes: ByteArray,
endorsementBytes: ByteArray
logEntry: RekorLogEntry,
publicKeyBytes: ByteArray,
endorsementBytes: ByteArray,
): Optional<Failure> {
val rekorSigVer = verifyRekorSignature(logEntry, publicKeyBytes)
return if (rekorSigVer.isPresent()) {
Expand Down Expand Up @@ -108,28 +108,31 @@ object LogEntryVerifier {
fun verifyRekorBody(body: RekorLogEntry.Body, contentBytes: ByteArray): Optional<Failure> {
if (body.spec.signature.format != "x509") {
return failure(
"unsupported signature format: ${body.spec.signature.format} only x509 is supported")
"unsupported signature format: ${body.spec.signature.format} only x509 is supported"
)
}

// For now, we only support `sha256` as the hashing algorithm.
if (body.spec.data.hash.algorithm != "sha256") {
return failure(
"unsupported hash algorithm: ${body.spec.data.hash.algorithm} only sha256 is supported",
"unsupported hash algorithm: ${body.spec.data.hash.algorithm} only sha256 is supported"
)
}

// Content digest must match the hash mentioned in the body.
val digest = sha256Hex(contentBytes)
if (body.spec.data.hash.value != digest) {
return failure(
"SHA2-256 digest of contents ($digest) differs from that in Rekor entry body (${body.spec.data.hash.value})")
"SHA2-256 digest of contents ($digest) differs from that in Rekor entry body (${body.spec.data.hash.value})"
)
}
val signatureBytes = Base64.getDecoder().decode(body.spec.signature.content)
val publicKeyBytes =
SignatureVerifier.convertPemToRaw(
Base64.getDecoder()
.decode(body.spec.signature.publicKey.content)
.toString(StandardCharsets.UTF_8))
SignatureVerifier.convertPemToRaw(
Base64.getDecoder()
.decode(body.spec.signature.publicKey.content)
.toString(StandardCharsets.UTF_8)
)
return SignatureVerifier.verify(signatureBytes, publicKeyBytes, contentBytes)
}

Expand Down
68 changes: 34 additions & 34 deletions java/src/main/java/com/google/oak/verification/RekorLogEntry.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ import java.util.Base64
* https://github.com/sigstore/rekor/blob/2978cdc26fdf8f5bfede8459afd9735f0f231a2a/pkg/generated/models/log_entry.go#L89
*/
data class RekorLogEntry(
// package-private for testing
val logEntry: LogEntry
// package-private for testing
val logEntry: LogEntry
) {
// The following nested classes represent a subset of Rekor types defined in
// <https://github.com/sigstore/rekor/tree/2978cdc26fdf8f5bfede8459afd9735f0f231a2a/pkg/generated/models>.
Expand All @@ -37,23 +37,23 @@ data class RekorLogEntry(
// clients are not expected to instantiate them directly. The fields are not
// explicitly made final to allow instantiation with Gson.
data class LogEntry(
/** We cannot directly use the type `Body` here, since body is Base64-encoded. */
val body: String,

/**
* Unmarshaled body of this LogEntry. It is declared as a transient field, so that it is
* excluded when serializing and deserializing instances of LogEntry.
*/
val integratedTime: Long,

/**
* The SHA2-256 hash of the DER-encoded public key for the log at the time the entry was
* included in the log. Pattern: ^[0-9a-fA-F]{64}$
*/
val logID: String,

/** Minimum: 0 */
var logIndex: Long,
/** We cannot directly use the type `Body` here, since body is Base64-encoded. */
val body: String,

/**
* Unmarshaled body of this LogEntry. It is declared as a transient field, so that it is
* excluded when serializing and deserializing instances of LogEntry.
*/
val integratedTime: Long,

/**
* The SHA2-256 hash of the DER-encoded public key for the log at the time the entry was
* included in the log. Pattern: ^[0-9a-fA-F]{64}$
*/
val logID: String,

/** Minimum: 0 */
var logIndex: Long,
) {
@kotlin.jvm.Transient var bodyObject: Body? = null

Expand Down Expand Up @@ -101,14 +101,14 @@ data class RekorLogEntry(
* <https:></https:>//github.com/sigstore/rekor/blob/2978cdc26fdf8f5bfede8459afd9735f0f231a2a/pkg/generated/models/rekord_v001_schema.go#L383>
*/
data class GenericSignature(
/** Base64 content that is signed. */
val content: String,
/** Base64 content that is signed. */
val content: String,

/** Signature format, e.g., x509. */
val format: String,
/** Signature format, e.g., x509. */
val format: String,

/** Public key associated with the signing key that generated this signature. */
val publicKey: PublicKey,
/** Public key associated with the signing key that generated this signature. */
val publicKey: PublicKey,
)

/**
Expand All @@ -118,8 +118,8 @@ data class RekorLogEntry(
* <https:></https:>//github.com/sigstore/rekor/blob/2978cdc26fdf8f5bfede8459afd9735f0f231a2a/pkg/generated/models/rekord_v001_schema.go#L551.>
*/
data class PublicKey(
/** Base64 content of a public key. */
val content: String
/** Base64 content of a public key. */
val content: String
)

/**
Expand All @@ -131,8 +131,8 @@ data class RekorLogEntry(
* <https:></https:>//github.com/sigstore/rekor/blob/2978cdc26fdf8f5bfede8459afd9735f0f231a2a/pkg/generated/models/log_entry.go#L341>.
*/
internal data class LogEntryVerification(
/** Base64-encoded signature over the body, integratedTime, logID, and logIndex. */
val signedEntryTimestamp: String
/** Base64-encoded signature over the body, integratedTime, logID, and logIndex. */
val signedEntryTimestamp: String
)

val body: Body?
Expand All @@ -155,11 +155,11 @@ data class RekorLogEntry(
// Use a default Gson instance to parse JSON strings into Java objects.
val gson: Gson = GsonBuilder().create()
val entryMap: Map<String, Any> =
try {
gson.fromJson(json, object : TypeToken<Map<String?, Any?>?>() {}.getType())
} catch (e: JsonSyntaxException) {
throw IllegalArgumentException(e)
}
try {
gson.fromJson(json, object : TypeToken<Map<String?, Any?>?>() {}.getType())
} catch (e: JsonSyntaxException) {
throw IllegalArgumentException(e)
}

require(entryMap.size == 1) {
"Expected exactly one entry in the json-formatted Rekor log entry, found ${
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ import com.google.gson.GsonBuilder
* the /api/v1/log/publicKey Rest API. For [sigstore.dev], it is a PEM-encoded x509/PKIX public key.
*/
data class RekorSignatureBundle(
/**
* Canonicalized JSON representation, based on RFC 8785 rules, of a subset of a Rekor LogEntry
* fields that are signed to generate `signedEntryTimestamp` (also a field in the Rekor
* LogEntry). These fields include body, integratedTime, logID and logIndex.
*/
val canonicalized: String,
/** Base64-encoded signature over the canonicalized JSON document. */
val base64Signature: String,
/**
* Canonicalized JSON representation, based on RFC 8785 rules, of a subset of a Rekor LogEntry
* fields that are signed to generate `signedEntryTimestamp` (also a field in the Rekor LogEntry).
* These fields include body, integratedTime, logID and logIndex.
*/
val canonicalized: String,
/** Base64-encoded signature over the canonicalized JSON document. */
val base64Signature: String,
) {

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ object SignatureVerifier {
* @return empty if the verification succeeds, or a failure otherwise
*/
fun verify(
signatureBytes: ByteArray,
publicKeyBytes: ByteArray,
contentBytes: ByteArray
signatureBytes: ByteArray,
publicKeyBytes: ByteArray,
contentBytes: ByteArray,
): Optional<Failure> {
if (signatureBytes.isEmpty()) {
return failure("empty signature")
Expand Down Expand Up @@ -100,12 +100,12 @@ object SignatureVerifier {

/** Makes a plausible guess whether the public key is in PEM format. */
fun looksLikePem(maybePem: String): Boolean =
maybePem.trim().run { startsWith(PEM_HEADER) && endsWith(PEM_FOOTER) }
maybePem.trim().run { startsWith(PEM_HEADER) && endsWith(PEM_FOOTER) }

/** Converts a public key from PEM (on disk format) to raw binary format. */
fun convertPemToRaw(pem: String): ByteArray {
val trimmed: String =
pem.replace(PEM_HEADER, "").replace(PEM_FOOTER, "").replace("\n", "").trim()
pem.replace(PEM_HEADER, "").replace(PEM_FOOTER, "").replace("\n", "").trim()
return Base64.getDecoder().decode(trimmed)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,15 @@
package com.google.oak.verification

import java.io.File
import org.junit.Test
import kotlin.test.assertFalse
import kotlin.test.assertTrue
import org.junit.Test

class LogEntryVerifierTest {
private val logEntryBytes = File(LOG_ENTRY_PATH).readBytes()
private val logEntry = RekorLogEntry.createFromJson(logEntryBytes)
private val publicKeyBytes = SignatureVerifier.convertPemToRaw(
File(REKOR_PUBLIC_KEY_PATH).readText()
)
private val publicKeyBytes =
SignatureVerifier.convertPemToRaw(File(REKOR_PUBLIC_KEY_PATH).readText())
private val endorsementBytes = File(ENDORSEMENT_PATH).readBytes()

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
//
package com.google.oak.verification

import kotlin.test.assertEquals
import java.io.File
import kotlin.test.assertEquals
import kotlin.test.assertTrue
import org.junit.Test

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ class SignatureVerifierTest {

@Test
fun testVerifySucceeds() {
val failure = SignatureVerifier.verify(signatureBytes, publicKeyBytes, contentBytes)
val failure = SignatureVerifier.verify(signatureBytes, publicKeyBytes, contentBytes)
assertFalse(failure.isPresent())
}

@Test
fun testVerifyFailsWithManipulatedSignature() {
signatureBytes[signatureBytes.size / 2]++
val failure = SignatureVerifier.verify(signatureBytes, publicKeyBytes, contentBytes)
val failure = SignatureVerifier.verify(signatureBytes, publicKeyBytes, contentBytes)
assertTrue(failure.isPresent())
}

Expand Down
1 change: 1 addition & 0 deletions linter/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ rust_library(
"tools/buildifier.rs",
"tools/clang_format.rs",
"tools/hadolint.rs",
"tools/ktfmt.rs",
"tools/lib.rs",
"tools/markdownlint.rs",
"tools/prettier.rs",
Expand Down
1 change: 1 addition & 0 deletions linter/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ fn main() {
counts += context.lint(tools::buildifier::BuildifierTool {});
counts += context.lint(tools::clang_format::ClangFormatTool {});
counts += context.lint(tools::hadolint::HadolintTool {});
counts += context.lint(tools::ktfmt::KtfmtTool {});
counts += context.lint(tools::prettier::PrettierTool {});
counts += context.lint(tools::shell_check::ShellCheckTool {});
counts += context.lint(tools::rustfmt::RustfmtTool {});
Expand Down
2 changes: 1 addition & 1 deletion linter/tools/buildifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,6 @@ impl linter::LinterTool for BuildifierTool {
}

fn fix(&self, path: &Path) -> anyhow::Result<linter::Outcome> {
super::linter_command("buildifier", &["-mode=fix", "-lint=fix", "-v"], path)
super::linter_command("buildifier", &["-mode=fix", "-lint=fix"], path)
}
}
41 changes: 41 additions & 0 deletions linter/tools/ktfmt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// Copyright 2024 The Project Oak Authors
//
// 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.
//

use std::path::Path;

use super::{has_extension, QuietSuccess};

pub struct KtfmtTool {}

impl linter::LinterTool for KtfmtTool {
const NAME: &'static str = "Kotlin Format";
const SUPPORTS_FIX: bool = true;

fn accept(&self, path: &Path) -> anyhow::Result<bool> {
Ok(has_extension(path, &["kt"]))
}

fn check(&self, path: &Path) -> anyhow::Result<linter::Outcome> {
super::linter_command("ktfmt", &["--google-style", "--dry-run"], path)
}

fn fix(&self, path: &Path) -> anyhow::Result<linter::Outcome> {
super::linter_command("ktfmt", &["--google-style"], path)
// Ktfmt is noisy when fixing, outputting filenames whether something was done or not,
// with no useful info.
.map(|outcome| outcome.quiet_success())
}
}
14 changes: 14 additions & 0 deletions linter/tools/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub mod build_license;
pub mod buildifier;
pub mod clang_format;
pub mod hadolint;
pub mod ktfmt;
pub mod markdownlint;
pub mod prettier;
pub mod rustfmt;
Expand Down Expand Up @@ -50,3 +51,16 @@ fn contents_starts_with(path: &Path, bytes: &[u8]) -> anyhow::Result<bool> {
fn linter_command(command: &str, args: &[&str], path: &Path) -> anyhow::Result<linter::Outcome> {
Command::new(command).args(args).arg(path.to_str().unwrap()).try_into()
}

trait QuietSuccess {
fn quiet_success(self) -> linter::Outcome;
}

impl QuietSuccess for linter::Outcome {
fn quiet_success(self) -> linter::Outcome {
match self {
linter::Outcome::Success(_) => linter::Outcome::Success("".to_string()),
x => x,
}
}
}

0 comments on commit 6a40d36

Please sign in to comment.