Skip to content

Commit 71778ee

Browse files
authored
Merge pull request #9 from flexca/release-preparation-1-0-0
Release preparation 1 0 0
2 parents 057396b + 6e9f05d commit 71778ee

96 files changed

Lines changed: 7328 additions & 1631 deletions

File tree

Some content is hidden

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

.github/workflows/maven-test.yml renamed to .github/workflows/continuous-integration.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Java CI with Maven
1+
name: Continuous Integration
22

33
on:
44
push:

CHANGELOG.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [1.0.0] - 2026-05-xx
9+
10+
### Added
11+
12+
#### Core — ASN.1 DER encoding
13+
14+
- JSON and YAML template support for describing binary structures
15+
- Parameterised placeholder syntax (`${name}`) with runtime value substitution
16+
- `optional` element flag — silently skips elements whose value is absent from the params map
17+
- Nested element bodies — the encoded output of one element can be the body of another
18+
- Scope-aware placeholder resolution: loop scope, group scope, and `global.` prefix for top-level access
19+
20+
**Structural tags**
21+
- `sequence` — DER SEQUENCE, ordered collection of child elements
22+
- `set` — DER SET, unordered collection
23+
24+
**Primitive tags**
25+
- `object_identifier` — dotted-decimal OID; supports `allowed_values` validation
26+
- `boolean` — DER BOOLEAN (`true``0xFF`, `false``0x00`)
27+
- `integer` — DER INTEGER, supports int / long / BigInteger values
28+
- `octet_string` — DER OCTET STRING; supports `min_length` / `max_length` constraints
29+
- `bit_string` — DER BIT STRING; supports `apply_padding` for named-bit-list types
30+
- `null` — DER NULL (`05 00`), no body required
31+
32+
**String tags** — all support `min_length`, `max_length`, and `allowed_values` constraints
33+
- `utf8_string` — full Unicode (UTF-8)
34+
- `printable_string` — PrintableString character set
35+
- `ia5_string` — 7-bit ASCII (IA5)
36+
- `visible_string` — printable ASCII (VisibleString)
37+
- `bmp_string` — UCS-2 / BMP plane (U+0000–U+FFFF)
38+
39+
**Time tags**
40+
- `generalized_time` — DER GeneralizedTime (required for dates ≥ 2050 per RFC 5280)
41+
- `utc_time` — DER UTCTime (required for dates before 2050 per RFC 5280)
42+
43+
**Context-tagging**
44+
- `tagged_object` — wraps body in a context-specific `[n]` tag; supports `implicit` and `explicit` tagging
45+
46+
#### Core — System elements
47+
48+
- `group` — pushes a named sub-object scope
49+
- `loop` — iterates over a list parameter, encoding the body element once per entry
50+
- `condition` — conditionally includes a child element based on a boolean expression
51+
- `reference` — resolves a named template from the registry and encodes it inline
52+
- `hex_to_bin` — decodes a hex string into raw bytes
53+
- `bin_to_hex` — encodes raw bytes as a hex string
54+
- `sha1` — computes the SHA-1 digest of the body bytes
55+
- `bit_map` — assembles a bitmask from a list of named boolean flags
56+
57+
#### Core — Extensibility
58+
59+
- `EnotRegistry` / `EnotRegistry.Builder` — registry for type specifications and named templates
60+
- `TypeSpecification` SPI — register custom element types without modifying the library
61+
- `Asn1TypeSpecification` — bundles all 16 ASN.1 tags; register once to enable all
62+
- `SystemTypeSpecification` — bundles all 8 system elements; register once to enable all
63+
64+
#### BER-TLV module
65+
66+
- Pluggable BER-TLV encoding support
67+
68+
#### Web tool
69+
70+
- Browser-based serialization playground (`web-tool` module)
71+
- Side-by-side Template and Params editors with YAML / JSON format switching
72+
- **Example Params** button — auto-generates a params skeleton from the current template
73+
- Base64-encoded output display
74+
- Inline error panel for template and serialization errors
75+
- Runnable via Docker or as a standalone JAR

README.md

Lines changed: 137 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -1,169 +1,183 @@
11
# eNot — Encoding Notations
22

3-
[![Build](https://github.com/flexca/eNot/actions/workflows/maven-test.yml/badge.svg)](https://github.com/flexca/eNot/actions/workflows/maven-test.yml)
4-
[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)
5-
[![Java](https://img.shields.io/badge/Java-17%2B-orange.svg)](https://openjdk.org/projects/jdk/17/)
3+
[![Build](https://github.com/flexca/eNot/actions/workflows/continuous-integration.yml/badge.svg)](https://github.com/flexca/eNot/actions/workflows/continuous-integration.yml)
4+
[![Maven Central](https://img.shields.io/maven-central/v/io.github.flexca/enot-core.svg)](https://central.sonatype.com/artifact/io.github.flexca/enot-core)
5+
[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)
6+
[![Java](https://img.shields.io/badge/Java-17%2B-blue)](https://openjdk.org)
67

7-
**eNot** (**Encoding Notations**) is a Java templating engine that turns plain **JSON or YAML** templates into binary-encoded data — primarily **ASN.1 DER**, with **BER-TLV** support demonstrating the design is not tied to a single format.
8+
**eNot** is a Java templating engine that transforms **JSON/YAML** structures into binary-encoded data (**ASN.1 DER**, **BER-TLV**, and more). By separating structure from values, eNot eliminates the fragility of hand-written encoding and provides a robust, maintainable way to generate complex binary payloads.
89

9-
---
10+
### Quick Start
11+
12+
#### 1. Add dependency
1013

11-
## The problem it solves
14+
Add the following dependency to your `pom.xml`:
1215

13-
Applications that produce structured binary data — PKI certificates, smart-card commands, device provisioning payloads, network protocol messages — typically choose between two bad options:
16+
```xml
17+
<dependency>
18+
<groupId>io.github.flexca</groupId>
19+
<artifactId>enot-core</artifactId>
20+
<version>1.0.0</version>
21+
</dependency>
22+
```
1423

15-
- A **heavy framework** that owns the structure and leaves no room to deviate
16-
- **Hand-written encoding code** that is fragile, hard to review, and impossible to reuse
24+
Or to `build.gradle`:
1725

18-
eNot takes a different approach: describe the binary structure as a plain text template, supply values at runtime, and let the engine handle the encoding.
26+
```groovy
27+
implementation 'io.github.flexca:enot-core:1.0.0'
28+
```
1929

20-
**JSON template:**
30+
#### 2. Create a template
31+
eNot templates mirror your data structure. For example, to encode this **ASN.1 definition**:
2132

22-
```json
23-
{
24-
"type": "asn.1",
25-
"attributes": { "tag": "utf8_string" },
26-
"body": "${common_name}"
33+
```asn1
34+
ExampleStructure ::= SET {
35+
data SEQUENCE {
36+
oid OBJECT IDENTIFIER,
37+
name UTF8String
38+
}
2739
}
2840
```
2941

30-
**Equivalent YAML template:**
31-
42+
You define the following **template.yaml**:
3243
```yaml
3344
type: asn.1
34-
attributes:
35-
tag: utf8_string
36-
body: "${common_name}"
45+
attributes: { tag: set }
46+
body:
47+
type: asn.1
48+
attributes: { tag: sequence }
49+
body:
50+
- type: asn.1
51+
attributes: { tag: object_identifier }
52+
body: "${my_oid}"
53+
- type: asn.1
54+
attributes: { tag: utf8_string }
55+
body: "${my_text}"
3756
```
57+
*Check out the [eNot reference](docs/enot.md) for a deep dive into elements and scoping.*
58+
3859
39-
**Serialize it:**
60+
#### 3. Encode your data
61+
Supply runtime values via `SerializationContext` to generate the binary output:
4062

4163
```java
42-
Enot enot = new Enot.Builder()
43-
.withRegistry(registry)
44-
.withJsonObjectMapper(new ObjectMapper())
45-
.build();
46-
47-
List<byte[]> der = enot.serialize(templateJson,
48-
new SerializationContext.Builder(objectMapper)
49-
.withParam("common_name", "Alice")
50-
.build());
64+
import io.github.flexca.enot.core.exception.EnotException;
65+
import io.github.flexca.enot.core.registry.EnotRegistry;
66+
import io.github.flexca.enot.core.serializer.context.SerializationContext;
67+
import io.github.flexca.enot.core.types.asn1.Asn1TypeSpecification;
68+
import io.github.flexca.enot.core.types.system.SystemTypeSpecification;
69+
import tools.jackson.databind.ObjectMapper;
70+
import tools.jackson.dataformat.yaml.YAMLFactory;
71+
72+
import java.util.Base64;
73+
import java.util.List;
74+
75+
public class EnotQuickStart {
76+
77+
// YAML template:
78+
private static final String ENOT_TEMPLATE = """
79+
type: asn.1
80+
attributes: { tag: set }
81+
body:
82+
type: asn.1
83+
attributes: { tag: sequence }
84+
body:
85+
- type: asn.1
86+
attributes: { tag: object_identifier }
87+
body: "${my_oid}"
88+
- type: asn.1
89+
attributes: { tag: utf8_string }
90+
body: "${my_text}"
91+
""";
92+
93+
public static void main(String[] args) {
94+
95+
// Jackson object mappers:
96+
ObjectMapper jsonObjectMapper = new ObjectMapper(); // Required if you are using JSON
97+
ObjectMapper yamlObjectMapper = new ObjectMapper(new YAMLFactory()); // Required if you are using YAML
98+
99+
// Registry allow to supply eNot type specification:
100+
EnotRegistry registry = new EnotRegistry.Builder()
101+
.withTypeSpecification(new SystemTypeSpecification()) // System elements - loops, conditions and other utilites
102+
.withTypeSpecification(new Asn1TypeSpecification()) // ASN.1 elements - require to serialize to ASN.1 DER
103+
.build();
104+
105+
// Enot - facade for parsing, serialization, etc:
106+
Enot enot = new Enot.Builder()
107+
.withRegistry(registry) // Adding registry
108+
.withJsonObjectMapper(jsonObjectMapper) // Add JSON ObjectMapper if you are using JSON templates
109+
.withYamlObjectMapper(yamlObjectMapper) // Add YAML ObjectMapper if you are using YAML templates
110+
.build();
111+
112+
// Serialization context - hold params to replace placeholders:
113+
SerializationContext serializationContext = new SerializationContext.Builder()
114+
.withParam("my_oid", "1.2.840.113549.1.1.1") // Value to replace ${my_oid} placeholder in template
115+
.withParam("my_text", "eNot") // Value to replace ${my_text} placeholder in template
116+
.build();
117+
118+
try {
119+
// Serializing template with params to binary:
120+
List<byte[]> result = enot.serialize(ENOT_TEMPLATE, serializationContext);
121+
// Encoding result to Base64:
122+
String resultBase64Encoded = Base64.getEncoder().encodeToString(result.get(0));
123+
// Printing Base64 encoded result:
124+
System.out.println("Result: " + resultBase64Encoded);
125+
} catch (EnotException e) {
126+
e.printStackTrace();
127+
}
128+
}
129+
}
51130
```
52131

53-
The engine resolves `${common_name}`, encodes the UTF-8 string as DER, and returns the bytes. No hard-coded structures. No framework lock-in.
132+
### Key Features
54133

55-
---
134+
- **Human-readable templates** — eNot templates are plain JSON or YAML files that mirror the binary structure they produce. They are easy to read, review, and version-control. Because templates are data, not code, they can be updated and reloaded at runtime without rebuilding the application. See [eNot format reference](docs/enot.md).
56135

57-
## Key features
136+
- **ASN.1 DER encoding** — Full support for DER-encoded ASN.1 structures: sequences, sets, OIDs, strings, integers, time types, context tagging, and more. See [ASN.1 elements](docs/asn1/index.md).
58137

59-
| Feature | Description |
60-
|---------|-------------|
61-
| **JSON & YAML** | Both formats are supported out of the box — the engine detects which one you pass |
62-
| **Loops** | Iterate a template body over a list of parameters — one encoded output per entry |
63-
| **Conditions** | Encode a body only when an expression is true — cover multiple encoding variants in one template |
64-
| **References** | Include one template inside another at parse time — compose large structures from small, testable pieces |
65-
| **Expression engine** | Arithmetic, comparison, logical operators, and built-in functions for date and binary operations |
66-
| **Extensible** | Any binary format plugs in via `EnotRegistry``asn.1` and `ber-tlv` are two separate examples |
67-
| **Error reporting** | All parse errors are collected and reported together, not one at a time |
138+
- **BER-TLV support** — BER-TLV encoding is available as a plug-in module, demonstrating that the engine is not tied to ASN.1. See [ber-tlv module](ber-tlv/README.md).
68139

69-
---
140+
- **Control logic** — [`loop`](docs/system/loop.md) and [`condition`](docs/system/condition.md) system elements make templates truly flexible: iterate over parameter lists, include elements conditionally, and handle variable-length structures without any code changes.
70141

71-
## Loops, conditions, and composition
142+
- **Composition** — Break large templates into smaller, named pieces and reuse them with the [`reference`](docs/system/reference.md) system element. Build complex structures from tested, independently maintained parts.
72143

73-
Binary structures are rarely flat. eNot handles this with built-in control-flow elements.
144+
- **Extensibility** — The type system is open: register your own element types alongside the built-in ones. See [Adding a new eNot element type](docs/add-new-element-type.md).
74145

75-
**Loop** — any repeated structure maps directly to a loop:
146+
- **Interactive web tool** — Try eNot instantly in the browser without writing any Java. The web tool ships as a Docker image with a side-by-side template/params editor, YAML/JSON switching, and one-click example generation. See [web-tool](web-tool/README.md).
76147

77-
```json
78-
{
79-
"type": "system",
80-
"attributes": { "kind": "loop", "items_name": "dns_name" },
81-
"body": {
82-
"type": "asn.1",
83-
"attributes": { "tag": "tagged_object", "implicit": 2 },
84-
"body": {
85-
"type": "asn.1",
86-
"attributes": { "tag": "ia5_string" },
87-
"body": "${value}"
88-
}
89-
}
90-
}
91-
```
148+
- **Detailed documentation** — Every element type, attribute, and constraint is documented with examples. See [Documentation](docs/index.md).
92149

93-
**Condition** — a single template covering multiple encoding variants. This example follows RFC 5280, which requires dates before 2050 to use `UTCTime` and dates from 2050 onward to use `GeneralizedTime`:
94-
95-
```json
96-
[
97-
{
98-
"type": "system",
99-
"attributes": { "kind": "condition", "expression": "${expires_on} < '2050-01-01T00:00:00Z'" },
100-
"body": { "type": "asn.1", "attributes": { "tag": "utc_time" }, "body": "${expires_on}" }
101-
},
102-
{
103-
"type": "system",
104-
"attributes": { "kind": "condition", "expression": "${expires_on} >= '2050-01-01T00:00:00Z'" },
105-
"body": { "type": "asn.1", "attributes": { "tag": "generalized_time" }, "body": "${expires_on}" }
106-
}
107-
]
108-
```
109-
110-
**References** let one template include another by identifier at parse time, so large or complex structures can be assembled from smaller, independently testable pieces.
150+
> [!NOTE]
151+
> **eNot is designed for small, structured payloads** — certificate fields, extensions, smart-card commands, and similar structures that are kilobytes in size. The engine holds the entire structure in memory during serialization. It is not suitable for large binary payloads (hundreds of megabytes), and using it for such workloads — especially under concurrent load — may cause out-of-memory errors.
111152

112153
---
113154

114-
## Why plain text templates?
155+
### Modules
115156

116-
- **Version-controlled** — templates are diff-able and reviewable in a pull request like any other source file
117-
- **Structure mirrors output** — a `sequence` wrapping a `set` wrapping a `utf8_string` looks exactly like that in the template tree
118-
- **Runtime values stay separate** — the template describes *shape*; the `SerializationContext` provides *values*; neither knows about the other
119-
- **Format-agnostic core** — the same parser and serializer infrastructure drives both `asn.1` and `ber-tlv`; new formats plug in through `EnotRegistry` without touching the engine
157+
| Module | Artifact | Description |
158+
|--------|----------|-------------|
159+
| `core` | `enot-core` | Parser, serializer, expression engine, type registry, and all built-in element types (ASN.1 DER + system elements). The only dependency needed for most use cases. |
160+
| `ber-tlv` | `enot-ber-tlv` | BER-TLV encoding support. A plug-in module that demonstrates the extensible type system beyond ASN.1. |
161+
| `web-tool` | — | Browser-based interactive playground for evaluating eNot templates. Not a library dependency — run it via Docker or as a standalone JAR. |
120162

121163
---
122164

123-
## Status
165+
### Contributing
124166

125-
> **eNot is not yet published to Maven Central.** Build locally first:
126-
> ```
127-
> git clone https://github.com/flexca/eNot.git
128-
> cd eNot
129-
> mvn install
130-
> ```
131-
> Then reference the snapshot version — see [Quick Start](docs/quick-start.md) for the full setup.
167+
Issues and pull requests are welcome. If you find a bug, have a feature request, or want to add support for a new encoding format, please open an issue on [GitHub](https://github.com/flexca/eNot/issues).
132168

133169
---
134170

135-
## Modules
171+
### Acknowledgements
136172

137-
| Module | Description |
138-
|--------|-------------|
139-
| `core` | Parser, serializer, expression engine, type registry |
140-
| `ber-tlv` | BER-TLV type extension — plug-in format example |
141-
142-
---
173+
eNot is built on top of several excellent open-source libraries:
143174

144-
## Building
145-
146-
Requirements: **Java 17+**, **Maven 3.8+**
147-
148-
```
149-
mvn install # build everything and run all tests
150-
mvn -pl core test # run only core module tests
151-
```
152-
153-
---
154-
155-
## Documentation
156-
157-
| Document | Description |
158-
|----------|-------------|
159-
| [Quick Start](docs/quick-start.md) | Build the registry, write a template, serialize |
160-
| [Format overview](docs/format/README.md) | Element structure, values, placeholders, scoping |
161-
| [ASN.1 elements](docs/format/asn1.md) | All supported tags and accepted body types |
162-
| [System elements](docs/format/system.md) | loop, condition, group, reference, bit_map, sha1, … |
163-
| [Expression syntax](docs/format/expressions.md) | Operators, functions, type rules |
175+
- **[Bouncy Castle](https://www.bouncycastle.org/)** — ASN.1 DER encoding and cryptographic primitives. The core of what makes eNot's ASN.1 support possible.
176+
- **[Jackson](https://github.com/FasterXML/jackson)** — JSON and YAML parsing for template loading.
177+
- **[Apache Commons Lang](https://commons.apache.org/proper/commons-lang/)** and **[Apache Commons Collections](https://commons.apache.org/proper/commons-collections/)** — utility support throughout the library.
178+
- **[Lombok](https://projectlombok.org/)** — boilerplate reduction in the Java source.
164179

165180
---
166181

167-
## License
168-
169-
Apache License 2.0 — see [LICENSE](LICENSE).
182+
### License
183+
Apache License 2.0 — see [LICENSE](LICENSE).

0 commit comments

Comments
 (0)