Skip to content

Commit f03fae3

Browse files
OPEN-85: General API refactoring to ensure consistent design and naming across all modules (#21)
- Introduced code review templates to standardize review practices. - Provided architectural guidelines to ensure consistency in implementation. - Added constant time equals for secure key materials. - Overall refactoring and alignments of the API.
1 parent 1721a9c commit f03fae3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+3171
-1123
lines changed

AGENTS.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<!--
2+
SPDX-FileCopyrightText: Copyright 2025 gematik GmbH
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
18+
*******
19+
20+
For additional notes and disclaimer from gematik and in case of changes by gematik,
21+
find details in the "Readme" file.
22+
-->
23+
24+
# Repository Guidelines
25+
26+
## Project Structure & Modules
27+
- Rust workspace root at `./`, member crates under `core-modules/` (`asn1`, `crypto`, `crypto-openssl-sys`, `healthcard`).
28+
- Kotlin/JVM bindings and examples live in `core-modules-kotlin/` (`healthcard`, `sample-app`).
29+
- Documentation is in `docs/` (see `docs/architecture/README.md` and `docs/interop/jvm.md` for deeper details).
30+
31+
## Build, Test & Development
32+
- Build all Rust crates: `cargo build` (from the repository root).
33+
- Run Rust tests: `cargo test` (add `-p <crate>` to limit scope, e.g. `cargo test -p healthcard`).
34+
- Kotlin bindings and sample: from `core-modules-kotlin/`, use `./gradlew :healthcard:build` and `./gradlew :sample-app:run`.
35+
- Before pushing, ensure `cargo +nightly fmt` and `cargo clippy --all-targets --all-features` are clean.
36+
37+
## Coding Style & Naming
38+
- Rust is formatted with `rustfmt` (see `rustfmt.toml`), 4-space indentation, max line width 120.
39+
- Use expressive, domain-oriented names and follow existing module layout (e.g. `exchange`, `command`, `identifier` in `healthcard`).
40+
- TODOs must reference a ticket: `// TODO OPEN-1234: Brief description`.
41+
- New files must carry SPDX headers and license annotations consistent with existing files.
42+
43+
## Testing Guidelines
44+
- Prefer Rust unit tests close to the code (inline `#[cfg(test)] mod tests`).
45+
- For cryptography and parsing, use known-good test vectors where possible.
46+
- JVM code uses Kotlin test/JUnit; run with `./gradlew test` (or module-specific tasks such as `:sample-app:test`).
47+
- Aim to keep or improve test coverage for modified modules and cover edge cases relevant to healthcard flows.
48+
49+
## Commit & Pull Request Guidelines
50+
- Use ticket-prefixed commit messages, e.g. `OPEN-123: Short, imperative summary`.
51+
- Each PR should include: a short description, motivation/linked issue, and notes on testing performed.
52+
- Call out breaking API changes and update release notes or changelogs where applicable.
53+
- Keep PRs focused and small; follow existing patterns rather than introducing new architectural concepts without prior discussion.
54+
55+
## Security & Configuration
56+
- Review `SECURITY.md` for responsible disclosure and security expectations.
57+
- Be mindful of handling keys, secrets, and card data; never commit real credentials or production traces.
58+
- When in doubt about crypto or card-IO changes, seek review from someone familiar with the respective module.
59+
60+
## Agent & AI Review Instructions
61+
- When using AI tools for refactoring or review, follow the crate-specific templates: `core-modules/asn1/AGENTS_REVIEW_TEMPLATE.md`, `core-modules/crypto/AGENTS_REVIEW_TEMPLATE.md`, and `core-modules/healthcard/AGENTS_REVIEW_TEMPLATE.md`.
62+
- Treat these templates as additional constraints on top of this document: keep changes incremental, behavior-preserving, and aligned with the security and architectural guidance they provide.

core-modules-kotlin/sample-app/src/main/kotlin/de/gematik/openhealth/sample/PcscCardChannel.kt

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,13 @@ package de.gematik.openhealth.sample
2323

2424
import de.gematik.openhealth.healthcard.CardChannel
2525
import de.gematik.openhealth.healthcard.CardChannelException
26-
import de.gematik.openhealth.healthcard.TrustedChannelException
26+
import de.gematik.openhealth.healthcard.CommandApdu
27+
import de.gematik.openhealth.healthcard.HealthCardResponseStatus
28+
import de.gematik.openhealth.healthcard.ResponseApdu
2729
import javax.smartcardio.CardChannel as PcscChannel
2830
import javax.smartcardio.CardException
2931
import javax.smartcardio.CommandAPDU
32+
import kotlin.collections.joinToString
3033

3134
/**
3235
* Adapts the javax.smartcardio channel so it can be consumed by the UniFFI generated API.
@@ -40,16 +43,16 @@ class PcscCardChannel(
4043
return historicalBytes != null && historicalBytes.size > 15
4144
}
4245

43-
override fun transmit(command: ByteArray): ByteArray {
46+
override fun transmit(command: CommandApdu): ResponseApdu {
4447
try {
45-
val response = delegate.transmit(CommandAPDU(command))
46-
return response.bytes
48+
val response = delegate.transmit(CommandAPDU(command.toBytes()))
49+
val sw = response.sw.toUShort()
50+
val status = if (sw.toInt() == 0x9000) HealthCardResponseStatus.SUCCESS else HealthCardResponseStatus.UNKNOWN_STATUS
51+
return ResponseApdu(sw = sw, status = status, data = response.data)
4752
} catch (ex: CardException) {
4853
throw CardChannelException.Transport(
49-
TrustedChannelException.Transport(
50-
code = 0u,
51-
reason = ex.message ?: "PC/SC transmit failed",
52-
)
54+
code = 0u,
55+
reason = ex.message ?: "PC/SC transmit failed",
5356
)
5457
}
5558
}

core-modules-kotlin/sample-app/src/main/kotlin/de/gematik/openhealth/sample/TrustedChannelCli.kt

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,15 @@
2121

2222
package de.gematik.openhealth.sample
2323

24+
import de.gematik.openhealth.healthcard.ApduException
25+
import de.gematik.openhealth.healthcard.CardAccessNumber
26+
import de.gematik.openhealth.healthcard.CommandApdu
27+
import de.gematik.openhealth.healthcard.TrustedChannelException
2428
import de.gematik.openhealth.healthcard.establishTrustedChannel
2529
import javax.smartcardio.Card
2630
import javax.smartcardio.CardTerminals
2731
import javax.smartcardio.TerminalFactory
32+
import kotlin.collections.joinToString
2833

2934
/**
3035
* Minimal demonstration that wires the trusted channel implementation into the PC/SC stack.
@@ -37,13 +42,24 @@ fun main() {
3742
?: System.getenv("CARD_ACCESS_NUMBER")
3843
?: error("Provide CARD_ACCESS_NUMBER env variable or -DcardAccessNumber=XXXXXX")
3944
val apdu = byteArrayOf(0x00, 0x84.toByte(), 0x00, 0x00, 0x08) // GET CHALLENGE default
45+
val can = try {
46+
CardAccessNumber.fromDigits(cardAccessNumber)
47+
} catch (ex: TrustedChannelException) {
48+
error("Invalid CAN: ${ex.message}")
49+
}
4050

4151
val card = openPcscCard()
4252
try {
43-
val trustedChannel = establishTrustedChannel(PcscCardChannel(card.basicChannel), cardAccessNumber)
53+
val trustedChannel = establishTrustedChannel(PcscCardChannel(card.basicChannel), can)
4454
println("Trusted channel established.")
45-
val response = trustedChannel.transmit(apdu)
46-
println("Trusted channel response: ${response.toHexString()}")
55+
val command = try {
56+
CommandApdu.fromBytes(apdu)
57+
} catch (ex: ApduException) {
58+
error("Failed to build command APDU: ${ex.message}")
59+
}
60+
val response = trustedChannel.transmit(command)
61+
val sw = "%04X".format(response.sw.toInt())
62+
println("Trusted channel response: SW=$sw, data=${response.data.toHexString()}, status=${response.status}")
4763
} finally {
4864
try {
4965
card.disconnect(false)
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
<!--
2+
SPDX-FileCopyrightText: Copyright 2025 gematik GmbH
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
18+
*******
19+
20+
For additional notes and disclaimer from gematik and in case of changes by gematik,
21+
find details in the "Readme" file.
22+
-->
23+
24+
# AI Code Review & Refactoring Template – `asn1` Crate
25+
26+
You are an expert Rust engineer with strong background in ASN.1, DER/BER encoding, parsing security, and safe library design. You are reviewing and (optionally) refactoring the `asn1` crate of the OpenHealth Core project.
27+
28+
The primary goal is to improve safety, correctness, clarity, and consistency without changing the externally visible behavior or published API contracts, unless explicitly noted.
29+
30+
When in doubt, favor **small, well-justified, incremental improvements** over large sweeping rewrites.
31+
32+
---
33+
34+
## 1. Context & Goals
35+
36+
- The `asn1` crate provides **ASN.1 primitives and utilities** for other crates (`crypto`, `healthcard`, …).
37+
- It deals with:
38+
- Tags, OIDs, and basic ASN.1 value types
39+
- Encoding and decoding logic (likely DER-like)
40+
- Date/time handling
41+
- Error types for parsing/encoding issues
42+
- **Goal:** A small, well-structured, predictable core with **robust parsing**, **clear errors**, and **no undefined behavior**.
43+
44+
Before making changes:
45+
46+
1. Scan `src/lib.rs` to understand the public API surface.
47+
2. Inspect modules such as `decoder.rs`, `encoder.rs`, `tag.rs`, `oid.rs`, `date_time.rs`, and `error.rs`.
48+
3. Identify which items are public and likely used by other crates.
49+
50+
---
51+
52+
## 2. General Review Checklist
53+
54+
Work through this checklist and propose changes where they add clear value and preserve behavior.
55+
56+
### 2.1 API & Structure
57+
58+
- [ ] Is the module structure (`lib.rs` + submodules) clear and cohesive?
59+
- [ ] Are public types and functions named consistently and descriptively?
60+
- [ ] Are there obvious internal details accidentally exposed as `pub`?
61+
- [ ] Are public types and functions documented sufficiently for downstream crate authors?
62+
- [ ] Do re-exports and visibility align with how the crate is intended to be used?
63+
64+
### 2.2 ASN.1 Correctness & Robustness
65+
66+
- [ ] Are **length fields** and **tag values** validated rigorously (e.g., no unchecked overflows or negative lengths)?
67+
- [ ] Does decoding handle malformed or truncated input safely (no panics, no indexing out of bounds)?
68+
- [ ] Are unknown or unsupported tags handled in a predictable way (e.g., explicit error variants)?
69+
- [ ] Is there any custom ASN.1 logic that should be better documented or encapsulated?
70+
- [ ] Are ASN.1 date/time encodings correctly handled (e.g., UTCTime vs GeneralizedTime, timezone handling)?
71+
- [ ] Are any implicit assumptions (e.g., encoding rules) clearly documented and consistently applied?
72+
73+
### 2.3 Error Handling
74+
75+
- [ ] Are all failures represented with meaningful error variants (using `thiserror`) instead of generic strings?
76+
- [ ] Are errors **non-panicking** for invalid input (i.e., malformed ASN.1 should return `Result::Err`, not `panic!`)?
77+
- [ ] Are error variants documented and named consistently?
78+
- [ ] Is error propagation clear and idiomatic (`?` operator, contextual information where helpful)?
79+
80+
### 2.4 Safety & Performance
81+
82+
- [ ] Is all code safe Rust? If `unsafe` is present, is it minimal, necessary, and well-documented?
83+
- [ ] Are there any potential integer overflows or unchecked arithmetic in length/tag handling?
84+
- [ ] Are slices and indexing operations guarded by explicit bounds checks or safe helpers?
85+
- [ ] Are `regex` usages justified and efficient (or could simpler parsing logic suffice)?
86+
- [ ] Are allocations and copies kept reasonable given typical data sizes?
87+
88+
### 2.5 Style & Consistency
89+
90+
- [ ] Is the code formatted according to `rustfmt` and consistent across modules?
91+
- [ ] Are naming conventions consistent (snake_case, CamelCase, acronyms, etc.)?
92+
- [ ] Is there duplication that could be factored into helpers without harming clarity?
93+
- [ ] Are module/file responsibilities clear (e.g., tags in `tag.rs`, OIDs in `oid.rs`)?
94+
95+
---
96+
97+
## 3. Refactoring Guidelines
98+
99+
When proposing refactors, follow these principles:
100+
101+
- **Preserve behavior and public API** unless there is an obvious bug or unsafe behavior.
102+
- Prefer **small, composable helpers** over deeply nested, complex functions.
103+
- Avoid introducing new dependencies without a strong justification.
104+
- Keep ASN.1 logic **explicit and well-documented**; avoid “magic numbers” without explanation.
105+
- Where possible, make parsing and encoding **table-driven** or declarative, but only if it simplifies the code.
106+
107+
### 3.1 Common Refactor Targets
108+
109+
- [ ] Simplify complex decoding/encoding functions by extracting small helper functions.
110+
- [ ] Replace manual error construction with consistent, typed error variants.
111+
- [ ] Remove redundant conversions or allocations (e.g., unnecessary `to_vec`).
112+
- [ ] Clarify edge-case handling with explicit branches and comments (especially around length calculations).
113+
- [ ] Ensure public functions return clear, composable types (e.g., `Result<_, Asn1Error>`).
114+
115+
---
116+
117+
## 4. Security-Specific Checks
118+
119+
Although `asn1` is not performing cryptography directly, it is **security-sensitive** as it parses inputs that may come from untrusted sources.
120+
121+
- [ ] Ensure no panics can be triggered by arbitrary input.
122+
- [ ] Ensure all indexing into slices/arrays is bounds checked.
123+
- [ ] Avoid `unwrap`, `expect`, and `panic!` in parsing paths; use robust error handling instead.
124+
- [ ] Watch for integer truncation or sign errors when converting lengths or indices.
125+
- [ ] Consider denial-of-service vectors (e.g., extremely large lengths); ensure the design allows callers to constrain inputs as needed.
126+
127+
If you find any potential vulnerability, describe:
128+
129+
1. The problematic code,
130+
2. A minimal proof-of-concept scenario, and
131+
3. A concrete, minimal fix.
132+
133+
---
134+
135+
## 5. Output Format
136+
137+
When you finish your review of the `asn1` crate, produce:
138+
139+
1. **High-level summary**
140+
- One paragraph summarizing overall code health, structure, and main risks.
141+
2. **Findings list**
142+
- A bullet list with: *[Severity]* – short title – file:line – concise explanation.
143+
3. **Concrete refactor suggestions**
144+
- For each suggestion, include:
145+
- The goal (e.g., “clarify error handling in decoder”),
146+
- A short rationale,
147+
- A **small, focused patch** or pseudo-code showing the change.
148+
149+
Avoid huge rewrites; prefer a sequence of small, reviewable improvements.
150+

0 commit comments

Comments
 (0)