diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6225757 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,50 @@ +############################### +# Git Line Endings # +############################### + +* text=auto + +# CRLF for Windows files. +*.{cmd,[cC][mM][dD]} text eol=crlf +*.{bat,[bB][aA][tT]} text eol=crlf +*.cs text eol=crlf diff=csharp + +# LF for Linux files. +*.sh text eol=lf + +# Common files +*.java text eol=lf diff=java +*.html text eol=lf diff=html +*.css text eol=lf +*.js text eol=lf +*.sql text eol=lf + +############################### +# Git Large File System (LFS) # +############################### + +# Archives +*.7z filter=lfs diff=lfs merge=lfs -text +*.br filter=lfs diff=lfs merge=lfs -text +*.gz filter=lfs diff=lfs merge=lfs -text +*.tar filter=lfs diff=lfs merge=lfs -text +*.zip filter=lfs diff=lfs merge=lfs -text + +# Documents +*.pdf filter=lfs diff=lfs merge=lfs -text +*.docx diff=astextplain merge=lfs -text + +# Images +*.gif filter=lfs diff=lfs merge=lfs -text +*.ico filter=lfs diff=lfs merge=lfs -text +*.jpg filter=lfs diff=lfs merge=lfs -text +*.pdf filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text +*.psd filter=lfs diff=lfs merge=lfs -text +*.webp filter=lfs diff=lfs merge=lfs -text + +# Fonts +*.woff2 filter=lfs diff=lfs merge=lfs -text + +# Other +*.exe filter=lfs diff=lfs merge=lfs -text diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml new file mode 100644 index 0000000..50885d2 --- /dev/null +++ b/.github/workflows/build-and-test.yml @@ -0,0 +1,9 @@ +name: Build and Test + +on: + pull_request: + branches: [main] + +jobs: + build-and-test: + uses: eclipse-keypop/keypop-actions/.github/workflows/reusable-build-and-test.yml@main # NOSONAR - Same organization, trusted source diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml new file mode 100644 index 0000000..b4776c4 --- /dev/null +++ b/.github/workflows/publish-release.yml @@ -0,0 +1,10 @@ +name: Publish Release package + +on: + release: + types: [published] + +jobs: + publish-release: + uses: eclipse-keypop/keypop-actions/.github/workflows/reusable-publish-release.yml@main # NOSONAR - Same organization, trusted source + secrets: inherit # NOSONAR - Same organization, trusted source diff --git a/.github/workflows/publish-snapshot.yml b/.github/workflows/publish-snapshot.yml new file mode 100644 index 0000000..85cdaa5 --- /dev/null +++ b/.github/workflows/publish-snapshot.yml @@ -0,0 +1,10 @@ +name: Publish Snapshot package + +on: + push: + branches: [main] + +jobs: + publish-snapshot: + uses: eclipse-keypop/keypop-actions/.github/workflows/reusable-publish-snapshot.yml@main # NOSONAR - Same organization, trusted source + secrets: inherit # NOSONAR - Same organization, trusted source diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fc74077 --- /dev/null +++ b/.gitignore @@ -0,0 +1,43 @@ +# Java class files +*.class + +# Generated files +gen/ +out/ +release/ + +# Gradle +.gradle/ +build/ + +# Eclipse +.classpath +.project +.settings/ +bin/ + +# IntelliJ IDEA +.idea/ +*.iml +*.iws +*.ipr + +# Visual Studio Code +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +# MacOS +.DS_Store + +# Linux +*~ + +# Windows +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db +*.stackdump \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..913757e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,9 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +[unreleased]: https://github.com/eclipse-keypop/keypop-calypso-certificate-java-api/compare/0.1.0...HEAD \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..faa735b --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,93 @@ +# Community Code of Conduct + +**Version 2.0 +January 1, 2023** + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as community members, contributors, Committers[^1], and Project Leads (collectively "Contributors") pledge to make participation in our projects and our community a harassment-free and inclusive experience for everyone. + +This Community Code of Conduct ("Code") outlines our behavior expectations as members of our community in all Eclipse Foundation activities, both offline and online. It is not intended to govern scenarios or behaviors outside of the scope of Eclipse Foundation activities. Nor is it intended to replace or supersede the protections offered to all our community members under the law. Please follow both the spirit and letter of this Code and encourage other Contributors to follow these principles into our work. Failure to read or acknowledge this Code does not excuse a Contributor from compliance with the Code. + +## Our Standards + +Examples of behavior that contribute to creating a positive and professional environment include: + +- Using welcoming and inclusive language; +- Actively encouraging all voices; +- Helping others bring their perspectives and listening actively. If you find yourself dominating a discussion, it is especially important to encourage other voices to join in; +- Being respectful of differing viewpoints and experiences; +- Gracefully accepting constructive criticism; +- Focusing on what is best for the community; +- Showing empathy towards other community members; +- Being direct but professional; and +- Leading by example by holding yourself and others accountable + +Examples of unacceptable behavior by Contributors include: + +- The use of sexualized language or imagery; +- Unwelcome sexual attention or advances; +- Trolling, insulting/derogatory comments, and personal or political attacks; +- Public or private harassment, repeated harassment; +- Publishing others' private information, such as a physical or electronic address, without explicit permission; +- Violent threats or language directed against another person; +- Sexist, racist, or otherwise discriminatory jokes and language; +- Posting sexually explicit or violent material; +- Sharing private content, such as emails sent privately or non-publicly, or unlogged forums such as IRC channel history; +- Personal insults, especially those using racist or sexist terms; +- Excessive or unnecessary profanity; +- Advocating for, or encouraging, any of the above behavior; and +- Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +With the support of the Eclipse Foundation employees, consultants, officers, and directors (collectively, the "Staff"), Committers, and Project Leads, the Eclipse Foundation Conduct Committee (the "Conduct Committee") is responsible for clarifying the standards of acceptable behavior. The Conduct Committee takes appropriate and fair corrective action in response to any instances of unacceptable behavior. + +## Scope + +This Code applies within all Project, Working Group, and Interest Group spaces and communication channels of the Eclipse Foundation (collectively, "Eclipse spaces"), within any Eclipse-organized event or meeting, and in public spaces when an individual is representing an Eclipse Foundation Project, Working Group, Interest Group, or their communities. Examples of representing a Project or community include posting via an official social media account, personal accounts, or acting as an appointed representative at an online or offline event. Representation of Projects, Working Groups, and Interest Groups may be further defined and clarified by Committers, Project Leads, or the Staff. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the Conduct Committee via conduct@eclipse-foundation.org. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. Without the explicit consent of the reporter, the Conduct Committee is obligated to maintain confidentiality with regard to the reporter of an incident. The Conduct Committee is further obligated to ensure that the respondent is provided with sufficient information about the complaint to reply. If such details cannot be provided while maintaining confidentiality, the Conduct Committee will take the respondent‘s inability to provide a defense into account in its deliberations and decisions. Further details of enforcement guidelines may be posted separately. + +Staff, Committers and Project Leads have the right to report, remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code, or to block temporarily or permanently any Contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. Any such actions will be reported to the Conduct Committee for transparency and record keeping. + +Any Staff (including officers and directors of the Eclipse Foundation), Committers, Project Leads, or Conduct Committee members who are the subject of a complaint to the Conduct Committee will be recused from the process of resolving any such complaint. + +## Responsibility + +The responsibility for administering this Code rests with the Conduct Committee, with oversight by the Executive Director and the Board of Directors. For additional information on the Conduct Committee and its process, please write to . + +## Investigation of Potential Code Violations + +All conflict is not bad as a healthy debate may sometimes be necessary to push us to do our best. It is, however, unacceptable to be disrespectful or offensive, or violate this Code. If you see someone engaging in objectionable behavior violating this Code, we encourage you to address the behavior directly with those involved. If for some reason, you are unable to resolve the matter or feel uncomfortable doing so, or if the behavior is threatening or harassing, please report it following the procedure laid out below. + +Reports should be directed to . It is the Conduct Committee’s role to receive and address reported violations of this Code and to ensure a fair and speedy resolution. + +The Eclipse Foundation takes all reports of potential Code violations seriously and is committed to confidentiality and a full investigation of all allegations. The identity of the reporter will be omitted from the details of the report supplied to the accused. Contributors who are being investigated for a potential Code violation will have an opportunity to be heard prior to any final determination. Those found to have violated the Code can seek reconsideration of the violation and disciplinary action decisions. Every effort will be made to have all matters disposed of within 60 days of the receipt of the complaint. + +## Actions +Contributors who do not follow this Code in good faith may face temporary or permanent repercussions as determined by the Conduct Committee. + +This Code does not address all conduct. It works in conjunction with our [Communication Channel Guidelines](https://www.eclipse.org/org/documents/communication-channel-guidelines/), [Social Media Guidelines](https://www.eclipse.org/org/documents/social_media_guidelines.php), [Bylaws](https://www.eclipse.org/org/documents/eclipse-foundation-be-bylaws-en.pdf), and [Internal Rules](https://www.eclipse.org/org/documents/ef-be-internal-rules.pdf) which set out additional protections for, and obligations of, all contributors. The Foundation has additional policies that provide further guidance on other matters. + +It’s impossible to spell out every possible scenario that might be deemed a violation of this Code. Instead, we rely on one another’s good judgment to uphold a high standard of integrity within all Eclipse Spaces. Sometimes, identifying the right thing to do isn’t an easy call. In such a scenario, raise the issue as early as possible. + +## No Retaliation + +The Eclipse community relies upon and values the help of Contributors who identify potential problems that may need to be addressed within an Eclipse Space. Any retaliation against a Contributor who raises an issue honestly is a violation of this Code. That a Contributor has raised a concern honestly or participated in an investigation, cannot be the basis for any adverse action, including threats, harassment, or discrimination. If you work with someone who has raised a concern or provided information in an investigation, you should continue to treat the person with courtesy and respect. If you believe someone has retaliated against you, report the matter as described by this Code. Honest reporting does not mean that you have to be right when you raise a concern; you just have to believe that the information you are providing is accurate. + +False reporting, especially when intended to retaliate or exclude, is itself a violation of this Code and will not be accepted or tolerated. + +Everyone is encouraged to ask questions about this Code. Your feedback is welcome, and you will get a response within three business days. Write to . + +## Amendments + +The Eclipse Foundation Board of Directors may amend this Code from time to time and may vary the procedures it sets out where appropriate in a particular case. + +### Attribution + +This Code was inspired by the [Contributor Covenant](https://www.contributor-covenant.org/), version 1.4, available [here](https://www.contributor-covenant.org/version/1/4/code-of-conduct/). + +[^1]: Capitalized terms used herein without definition shall have the meanings assigned to them in the Bylaws. \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..e20bf5e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,43 @@ +# Contributing to 'Eclipse Keypop' + +**Welcome to the Eclipse Keypop Community!** + +We are thrilled to have you onboard and look forward to your contributions. Whether you're a coder, designer, +documenter, or enthusiast, your involvement is invaluable to us. Here's how you can start contributing to Eclipse +Keypop: + +## Quick Start + +1. **Read the Contribution Guidelines**: Before starting, please visit our detailed contributing guide at the +[Eclipse Keypop Contribution Guide](https://keypop.org/community/contributing/). +This guide covers all the necessary information about contributing to the project. + +2. **Join the mailing list**: Connect with other contributors on our +[mailing list](https://accounts.eclipse.org/mailing-list/keypop-dev/). +It's a great place to ask questions, share ideas, and collaborate. + +3. **Explore Open Issues**: Check out the issues tab in our GitHub repository. + +## How to Contribute + +- **Code**: Submit pull requests with bug fixes, new features, and improvements. Make sure to follow the code style and +testing guidelines. + +- **Documentation**: Help us improve our documentation by fixing errors, adding examples, or writing tutorials. + +- **Feedback**: Share your experience using Eclipse Keypop. Feedback on usability, features, and your overall experience +is incredibly valuable. + +- **Community Support**: Answer questions on the community forums, help with user support, and participate in +discussions. + +## Need Help? + +If you have any questions or need assistance, please reach out on the +[mailing list](https://accounts.eclipse.org/mailing-list/keypop-dev/). + +--- + +_Your contribution is the key to the success of Eclipse Keypop. Let's build something amazing together!_ + +--- \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4958beb --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Calypso Networks Association + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/LICENSE_HEADER b/LICENSE_HEADER new file mode 100644 index 0000000..efc0ba1 --- /dev/null +++ b/LICENSE_HEADER @@ -0,0 +1,11 @@ +/* ************************************************************************************** + * Copyright (c) $YEAR Calypso Networks Association https://calypsonet.org/ + * + * See the NOTICE file(s) distributed with this work for additional information + * regarding copyright ownership. + * + * This program and the accompanying materials are made available under the terms of the + * MIT License which is available at https://opensource.org/licenses/MIT + * + * SPDX-License-Identifier: MIT + ************************************************************************************** */ \ No newline at end of file diff --git a/NOTICE.md b/NOTICE.md new file mode 100644 index 0000000..6d3755f --- /dev/null +++ b/NOTICE.md @@ -0,0 +1,34 @@ +# Notices for 'Eclipse Keypop' Java implementation + +This content is produced and maintained by the Eclipse Keypop project. + +* Project home: https://projects.eclipse.org/projects/iot.keypop + +## Supported platforms + +* Java 1.8 +* Android 7.0 Nougat API Level 24 + +## Trademarks + +* Eclipse Keypop and the Eclipse Keypop project are Trademarks of the Eclipse Foundation, Inc. +* Eclipse® is a Trademark of the Eclipse Foundation, Inc. +* Eclipse Foundation is a Trademark of the Eclipse Foundation, Inc. + +## Copyright + +All content is the property of the respective authors or their employers. +For more information regarding authorship of content, please consult the +listed source code repository logs. + +## Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the MIT License which is available at +https://opensource.org/license/mit/ + +SPDX-License-Identifier: MIT + +## Third-party Content + +No third-party content. \ No newline at end of file diff --git a/PUBLISHERS.yml b/PUBLISHERS.yml new file mode 100644 index 0000000..43a920a --- /dev/null +++ b/PUBLISHERS.yml @@ -0,0 +1,18 @@ +url: https://github.com/eclipse-keypop/keypop-calypso-certificate-java-api +organization: + name: Eclipse Keypop + url: https://keypop.org/ +licenses: + - name: MIT license + url: https://opensource.org/license/mit/ + distribution: repo +developers: + - name: Keypop Contributors + email: keypop-dev@eclipse.org +scm: + connection: scm:git:git://github.com/eclipse-keypop/keypop-calypso-certificate-java-api.git + developerConnection: scm:git:https://github.com/eclipse-keypop/keypop-calypso-certificate-java-api.git + url: https://github.com/eclipse-keypop/keypop-calypso-certificate-java-api +ciManagement: + system: Jenkins + url: https://ci.eclipse.org/keypop/job/Keypop/job/keypop-calypso-certificate-java-api/ diff --git a/README.md b/README.md index ef2136b..2d4e7a6 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,23 @@ -# keypop-calypso-certificate-java-api -Eclipse Keypop project repository containing a Java implementation of the 'Calypso Certificate API' standardized by the Calypso Networks association for ticketing terminal processing smart card +# Keypop Calypso Certificate Java API + +## Overview + +This repository contains a Java implementation aligned with the **Terminal Calypso Certificate** specifications +proposed by the [Calypso Networks Association](https://www.calypsonet.org). It defines the generic interfaces required +to create Calypso certificates. + +## Documentation & Contribution Guide + +The full documentation, including the **user guide**, **download information** and **contribution guide**, is available +on the Keypop website [keypop.org](https://keypop.org/). + +## API documentation + +API Javadoc is available [here](https://docs.keypop.org/keypop-calypso-certificate-java-api). + +UML class diagram is available +[here](https://terminal-api.calypsonet.org/apis/calypsonet-terminal-calypso-certificate-api/). + +## About the source code + +The code is built with **Gradle** and is compliant with **Java 1.6** in order to address a wide range of applications. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..d0a3a3d --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,9 @@ +# Security Policy + +This project implements the Eclipse Foundation Security Policy + +* https://www.eclipse.org/security + +## Reporting a Vulnerability + +Please report vulnerabilities to the Eclipse Foundation Security Team at security@eclipse.org \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..29814c0 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,193 @@ +/////////////////////////////////////////////////////////////////////////////// +// GRADLE CONFIGURATION +/////////////////////////////////////////////////////////////////////////////// + +plugins { + java + `maven-publish` + signing + id("com.diffplug.spotless") version "6.25.0" +} + +/////////////////////////////////////////////////////////////////////////////// +// APP CONFIGURATION +/////////////////////////////////////////////////////////////////////////////// + +dependencies { + testImplementation(platform("org.junit:junit-bom:5.12.2")) + testImplementation("org.junit.jupiter:junit-jupiter") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") + testImplementation("org.assertj:assertj-core:3.25.3") +} + +/////////////////////////////////////////////////////////////////////////////// +// STANDARD CONFIGURATION FOR JAVA PROJECTS +/////////////////////////////////////////////////////////////////////////////// + +if (project.hasProperty("releaseTag")) { + project.version = project.property("releaseTag") as String + println("Release mode: version set to ${project.version}") +} else { + println("Development mode: version is ${project.version}") +} + +val javaSourceLevel: String by project +val javaTargetLevel: String by project + +java { + sourceCompatibility = JavaVersion.toVersion(javaSourceLevel) + targetCompatibility = JavaVersion.toVersion(javaTargetLevel) + println("Compiling Java $sourceCompatibility to Java $targetCompatibility.") + withJavadocJar() + withSourcesJar() +} + +fun copyLicenseFiles() { + val metaInfDir = File(layout.buildDirectory.get().asFile, "resources/main/META-INF") + val licenseFile = File(project.rootDir, "LICENSE") + val noticeFile = File(project.rootDir, "NOTICE.md") + metaInfDir.mkdirs() + licenseFile.copyTo(File(metaInfDir, "LICENSE"), overwrite = true) + noticeFile.copyTo(File(metaInfDir, "NOTICE.md"), overwrite = true) +} + +tasks { + spotless { + java { + target("src/**/*.java") + licenseHeaderFile("${project.rootDir}/LICENSE_HEADER") + importOrder("java", "javax", "org", "com", "") + removeUnusedImports() + googleJavaFormat() + } + kotlinGradle { + target("**/*.kts") + ktfmt() + } + } + test { + useJUnitPlatform() + testLogging { events("passed", "skipped", "failed") } + } + javadoc { + dependsOn(processResources) + val javadocLogo = project.findProperty("javadoc.logo") as String + val javadocCopyright = project.findProperty("javadoc.copyright") as String + val titleProperty = project.findProperty("title") as String + (options as StandardJavadocDocletOptions).apply { + overview = "src/main/javadoc/overview.html" + windowTitle = "$titleProperty - ${project.version}" + header( + "
$javadocLogo $titleProperty - ${project.version}
") + docTitle("$titleProperty - ${project.version}") + use(true) + bottom(javadocCopyright) + encoding = "UTF-8" + charSet = "UTF-8" + if (JavaVersion.current().isJava11Compatible) { + addBooleanOption("html5", true) + addStringOption("Xdoclint:none", "-quiet") + } + } + doFirst { println("Generating Javadoc for ${project.name} version ${project.version}") } + } + jar { + dependsOn(processResources) + doFirst { copyLicenseFiles() } + manifest { + attributes( + mapOf( + "Implementation-Title" to (project.findProperty("title") as String), + "Implementation-Version" to project.version, + "Implementation-Vendor" to (project.findProperty("organization.name") as String), + "Implementation-URL" to (project.findProperty("project.url") as String), + "Specification-Title" to (project.findProperty("title") as String), + "Specification-Version" to project.version, + "Specification-Vendor" to (project.findProperty("organization.name") as String), + "Created-By" to + "${System.getProperty("java.version")} (${System.getProperty("java.vendor")})", + "Build-Jdk" to System.getProperty("java.version"))) + } + } + named("sourcesJar") { + doFirst { copyLicenseFiles() } + manifest { + attributes( + mapOf( + "Implementation-Title" to "${project.findProperty("title") as String} Sources", + "Implementation-Version" to project.version)) + } + } + named("javadocJar") { + dependsOn(javadoc) + doFirst { copyLicenseFiles() } + manifest { + attributes( + mapOf( + "Implementation-Title" to "${project.findProperty("title") as String} Documentation", + "Implementation-Version" to project.version)) + } + } +} + +publishing { + publications { + create("mavenJava") { + from(components["java"]) + pom { + name.set(project.findProperty("title") as String) + description.set(project.findProperty("description") as String) + url.set(project.findProperty("project.url") as String) + licenses { + license { + name.set(project.findProperty("license.name") as String) + url.set(project.findProperty("license.url") as String) + distribution.set(project.findProperty("license.distribution") as String) + } + } + developers { + developer { + name.set(project.findProperty("developer.name") as String) + email.set(project.findProperty("developer.email") as String) + } + } + organization { + name.set(project.findProperty("organization.name") as String) + url.set(project.findProperty("organization.url") as String) + } + scm { + connection.set(project.findProperty("scm.connection") as String) + developerConnection.set(project.findProperty("scm.developerConnection") as String) + url.set(project.findProperty("scm.url") as String) + } + ciManagement { + system.set(project.findProperty("ci.system") as String) + url.set(project.findProperty("ci.url") as String) + } + properties.set( + mapOf( + "project.build.sourceEncoding" to "UTF-8", + "maven.compiler.source" to javaSourceLevel, + "maven.compiler.target" to javaTargetLevel)) + } + } + } + repositories { + maven { + if (project.hasProperty("sonatypeURL")) { + url = uri(project.property("sonatypeURL") as String) + credentials { + username = project.property("sonatypeUsername") as String + password = project.property("sonatypePassword") as String + } + } + } + } +} + +signing { + if (project.hasProperty("releaseTag")) { + useGpgCmd() + sign(publishing.publications["mavenJava"]) + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..98c65e7 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,41 @@ +# Project Configuration +group = org.eclipse.keypop +title = Keypop Calypso Certificate API +description = API dedicated to the creation of Calypso Certificates +version = 0.1.0-SNAPSHOT + +# Java Configuration +javaSourceLevel = 1.8 +javaTargetLevel = 1.8 + +# UTF-8 required by javadoc for special characters (ex. copyright) with Java 11+ +org.gradle.jvmargs = "-Dfile.encoding=UTF-8" + +# Documentation Configuration +javadoc.logo = +javadoc.copyright = Copyright © Eclipse Foundation, Inc. All Rights Reserved. + +# Project URLs +project.url = https://github.com/eclipse-keypop/keypop-calypso-crypto-asymmetric-java-api + +# Organization +organization.name = Eclipse Keypop +organization.url = https://keypop.org/ + +# License +license.name = MIT license +license.url = https://opensource.org/license/mit/ +license.distribution = repo + +# Developers +developer.name = Keypop Contributors +developer.email = keypop-dev@eclipse.org + +# Source Control Management +scm.connection = scm:git:git://github.com/eclipse-keypop/keypop-calypso-crypto-asymmetric-java-api.git +scm.developerConnection = scm:git:https://github.com/eclipse-keypop/keypop-calypso-crypto-asymmetric-java-api.git +scm.url = https://github.com/eclipse-keypop/keypop-calypso-crypto-asymmetric-java-api + +# Continuous Integration +ci.system = GitHub Actions +ci.url = https://github.com/eclipse-keypop/keypop-calypso-crypto-asymmetric-java-api/actions diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..afba109 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..c7d437b --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.4-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..65dcd68 --- /dev/null +++ b/gradlew @@ -0,0 +1,244 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original 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 +# +# https://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. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..7f1d415 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,16 @@ +rootProject.name = "keypop-calypso-certificate-java-api" + +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + } +} + +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + mavenLocal() + mavenCentral() + } +} diff --git a/src/main/java/org/eclipse/keypop/calypso/certificate/CalypsoCaCertificateV1Generator.java b/src/main/java/org/eclipse/keypop/calypso/certificate/CalypsoCaCertificateV1Generator.java new file mode 100644 index 0000000..8adba0f --- /dev/null +++ b/src/main/java/org/eclipse/keypop/calypso/certificate/CalypsoCaCertificateV1Generator.java @@ -0,0 +1,172 @@ +/* ************************************************************************************** + * Copyright (c) 2024 Calypso Networks Association https://calypsonet.org/ + * + * See the NOTICE file(s) distributed with this work for additional information + * regarding copyright ownership. + * + * This program and the accompanying materials are made available under the terms of the + * MIT License which is available at https://opensource.org/licenses/MIT + * + * SPDX-License-Identifier: MIT + ************************************************************************************** */ +package org.eclipse.keypop.calypso.certificate; + +import java.security.interfaces.RSAPublicKey; + +/** + * Generates a certificate as a byte array conforming to version 1 of the Calypso CA certificate + * format. + * + * @since 0.1.0 + */ +public interface CalypsoCaCertificateV1Generator { + + /** + * Sets the public key of the CA. + * + *

This key is expected to be a 2048 bits RSA public key with a public exponent equal to 65537. + * It will be used for the verification of card certificates. + * + *

The associated reference is a 29-byte byte array. + * + * @param caPublicKeyReference A 29-byte byte array representing a reference to the CA's public + * key. + * @param caPublicKey The RSA public key of the CA (2048 bits, public exponent 65537). + * @return The current instance. + * @throws IllegalArgumentException If one of the provided argument is null or invalid. + * @since 0.1.0 + */ + CalypsoCaCertificateV1Generator withCaPublicKey( + byte[] caPublicKeyReference, RSAPublicKey caPublicKey); + + /** + * Sets the start date of the validity period of the certificate's public key. + * + *

No consistency test is performed on the values supplied, as they will be coded in BCD + * YYYYMMDD format in the certificate. + * + *

The start date is optional. If it is not defined, the certificate is not subject to a start + * date constraint. + * + * @param year The year of the start date (0-9999). + * @param month The month of the start date (1-99). + * @param day The day of the start date (1-99). + * @return The current instance. + * @throws IllegalArgumentException If any date parameter is out of range. + * @since 0.1.0 + */ + CalypsoCaCertificateV1Generator withStartDate(int year, int month, int day); + + /** + * Sets the end date of the validity period of the certificate's public key. + * + *

No consistency test is performed on the values supplied, as they will be coded in BCD + * YYYYMMDD format in the certificate. + * + *

The end date is optional. If it is not defined, the certificate is not subject to an end + * date constraint. + * + * @param year The year of the start date (0-9999). + * @param month The month of the start date (1-99). + * @param day The day of the start date (1-99). + * @return The current instance. + * @throws IllegalArgumentException If any date parameter is out of range. + * @since 0.1.0 + */ + CalypsoCaCertificateV1Generator withEndDate(int year, int month, int day); + + /** + * Restricts certificate validity to cards whose Application Identifier (AID) begins with the + * bytes provided. + * + *

This method allows you to specify an AID value to limit the applicability of the + * certificate. The certificate will only be valid for cards whose AID starts with the provided + * bytes. + * + *

The AID is optional. When not set, no restriction related to the card AID will be applied. + * + *

Important: + * + *

The aid field cannot contain only zero bytes. + * + *

The isTruncated field indicates whether the provided AID is truncated. If set to + * true, the certificate will be valid for cards whose AID starts with the provided bytes, + * even if the card's full AID is longer. If set to false, the certificate will only be + * valid for cards whose full AID exactly matches the provided bytes. + * + * @param aid The AID value as a 5 to 16 bytes byte array. Must not contain only zero bytes. + * @param isTruncated true if the provided AID is truncated, false otherwise. + * @return The current instance. + * @throws IllegalArgumentException If the provided AID is null, out of range, or contains only + * zero bytes. + * @since 0.1.0 + */ + CalypsoCaCertificateV1Generator withAid(byte[] aid, boolean isTruncated); + + /** + * Sets the CA rights for this card certificate, controlling which types of certificates can be + * authenticated. + * + *

The provided byte defines the following permissions: + * + *

    + *
  • Bits b7-b4: Reserved for future use (RFU). Must be set to 0. + *
  • Bits b3-b2: Card key certificates authentication right: + *
      + *
    • %00: CardCert authentication right not specified. + *
    • %01: CardCert authentication forbidden. + *
    • %10: CardCert authentication allowed. + *
    • %11: Reserved for future use. + *
    + *
  • Bits b1-b0: CA key certificates authentication right: + *
      + *
    • %00: CACert authentication right not specified. + *
    • %01: CACert authentication forbidden. + *
    • %10: CACert authentication allowed. + *
    • %11: Reserved for future use. + *
    + *
+ * + * The CA rights byte is optional. If not set, the default value is 0. + * + * @param caRights The byte representing the CA rights for this card certificate. + * @return The current instance. + * @throws IllegalArgumentException If the provided byte contains RFU values. + * @since 0.1.0 + */ + CalypsoCaCertificateV1Generator withCaRights(byte caRights); + + /** + * Sets the CA scope for this card certificate, defining the context in which the CA key pair can + * be used. + * + *

The provided byte specifies the allowed usage context: + * + *

    + *
  • %00: Scope restrictions not specified. + *
  • %01: Allowed only for development, tests, pilots, etc. (limited scope). + *
  • %FF: No scope restriction (full scope). + *
  • Other values: Reserved for future use (RFU). + *
+ * + * The CA scope byte is optional. If not set, the default value is 0. + * + * @param caScope The byte representing the CA scope for this card certificate. + * @return The current instance. + * @throws IllegalArgumentException If the provided byte contains RFU values. + * @since 0.1.0 + */ + CalypsoCaCertificateV1Generator withCaScope(byte caScope); + + /** + * Checks the consistency of the parameters, signs the certificate using the provided signer and + * returns a byte array representing the certificate ready to be injected into a card. + * + * @return A 384-byte byte array. + * @throws IllegalStateException If one of the required parameters is wrong or missing. + * @throws CertificateConsistencyException If the provided parameters are inconsistent. + * @throws CertificateSigningException If an error occurs during the signing process. + * @since 0.1.0 + */ + byte[] generate(); +} diff --git a/src/main/java/org/eclipse/keypop/calypso/certificate/CalypsoCardCertificateV1Generator.java b/src/main/java/org/eclipse/keypop/calypso/certificate/CalypsoCardCertificateV1Generator.java new file mode 100644 index 0000000..d999933 --- /dev/null +++ b/src/main/java/org/eclipse/keypop/calypso/certificate/CalypsoCardCertificateV1Generator.java @@ -0,0 +1,129 @@ +/* ************************************************************************************** + * Copyright (c) 2024 Calypso Networks Association https://calypsonet.org/ + * + * See the NOTICE file(s) distributed with this work for additional information + * regarding copyright ownership. + * + * This program and the accompanying materials are made available under the terms of the + * MIT License which is available at https://opensource.org/licenses/MIT + * + * SPDX-License-Identifier: MIT + ************************************************************************************** */ +package org.eclipse.keypop.calypso.certificate; + +/** + * Generates a certificate as a byte array conforming to version 1 of the Calypso card certificate + * format. + * + * @since 0.1.0 + */ +public interface CalypsoCardCertificateV1Generator { + + /** + * Sets the public key of the card, provided as a 64-byte array. + * + *

This key is expected to be on the secp256r1 elliptic curve. It will be used + * for the verification of card signatures. + * + * @param cardPublicKey The 64-byte array representing the public key on the + * secp256r1 curve. + * @return The current instance. + * @throws IllegalArgumentException If the provided key is null or out of range. + * @since 0.1.0 + */ + CalypsoCardCertificateV1Generator withCardPublicKey(byte[] cardPublicKey); + + /** + * Sets the start date of the validity period of the certificate's public key. + * + *

No consistency test is performed on the values supplied, as they will be coded in BCD + * YYYYMMDD format in the certificate. + * + *

The start date is optional. If it is not defined, the certificate is not subject to a start + * date constraint. + * + * @param year The year of the start date (0-9999). + * @param month The month of the start date (1-99). + * @param day The day of the start date (1-99). + * @return The current instance. + * @throws IllegalArgumentException If any date parameter is out of range. + * @since 0.1.0 + */ + CalypsoCardCertificateV1Generator withStartDate(int year, int month, int day); + + /** + * Sets the end date of the validity period of the certificate's public key. + * + *

No consistency test is performed on the values supplied, as they will be coded in BCD + * YYYYMMDD format in the certificate. + * + *

The end date is optional. If it is not defined, the certificate is not subject to an end + * date constraint. + * + * @param year The year of the start date (0-9999). + * @param month The month of the start date (1-99). + * @param day The day of the start date (1-99). + * @return The current instance. + * @throws IllegalArgumentException If any date parameter is out of range. + * @since 0.1.0 + */ + CalypsoCardCertificateV1Generator withEndDate(int year, int month, int day); + + /** + * Sets the AID of the autonomous PKI application of the target card. + * + *

The aid field cannot contain only zero bytes. + * + * @param aid The AID value as a 5 to 16 bytes byte array. Must not contain only zero bytes. + * @return The current instance. + * @throws IllegalArgumentException If the provided AID is null, out of range, or contains only + * zero bytes. + * @since 0.1.0 + */ + CalypsoCardCertificateV1Generator withCardAid(byte[] aid); + + /** + * Sets the serial number of the card for which the certificate is being generated. + * + * @param serialNumber The serial number of the card as an 8-byte byte array. + * @return The current instance. + * @throws IllegalArgumentException If the provided argument is null or out of range. + * @since 0.1.0 + */ + CalypsoCardCertificateV1Generator withCardSerialNumber(byte[] serialNumber); + + /** + * Sets the startup info of the card for which the certificate is being generated. + * + * @param startupInfo The 7-byte byte array representing the startup info for the card + * certificate. + * @return The current instance. + * @throws IllegalArgumentException If the provided argument is null or out of range. + * @since 0.1.0 + */ + CalypsoCardCertificateV1Generator withCardStartupInfo(byte[] startupInfo); + + /** + * Sets the index used to differentiate two card certificates generated with the same issuer + * public key reference for the same card. + * + *

The index is optional. By default, it is set to 0. + * + * @param index The index of the card certificate. + * @return The current instance. + * @since 0.1.0 + */ + CalypsoCardCertificateV1Generator withIndex(int index); + + /** + * Checks the consistency of the parameters, signs the certificate using the provided signer and + * returns a byte array representing the certificate ready to be injected into a card. + * + * @return A 316-byte byte array. + * @throws IllegalStateException If one of the required parameters is wrong or missing. + * @throws CertificateConsistencyException If the provided parameters are inconsistent. + * @throws CertificateSigningException If an error occurs during the signing process. + * @since 0.1.0 + */ + byte[] generate(); +} diff --git a/src/main/java/org/eclipse/keypop/calypso/certificate/CalypsoCertificateApiFactory.java b/src/main/java/org/eclipse/keypop/calypso/certificate/CalypsoCertificateApiFactory.java new file mode 100644 index 0000000..b41c779 --- /dev/null +++ b/src/main/java/org/eclipse/keypop/calypso/certificate/CalypsoCertificateApiFactory.java @@ -0,0 +1,75 @@ +/* ************************************************************************************** + * Copyright (c) 2024 Calypso Networks Association https://calypsonet.org/ + * + * See the NOTICE file(s) distributed with this work for additional information + * regarding copyright ownership. + * + * This program and the accompanying materials are made available under the terms of the + * MIT License which is available at https://opensource.org/licenses/MIT + * + * SPDX-License-Identifier: MIT + ************************************************************************************** */ +package org.eclipse.keypop.calypso.certificate; + +import org.eclipse.keypop.calypso.certificate.spi.CalypsoCertificateSignerSpi; + +/** + * Factory of CA and card certificate builders. + * + * @since 0.1.0 + */ +public interface CalypsoCertificateApiFactory { + + /** + * Returns the store where Calypso Certificate Authority (CA) certificates and optionally the + * private keys are injected and made available for certificate generation processes. + * + *

The store contains the necessary certificates and private keys required for generating + * Calypso CA and card certificates. + * + *

Use the methods provided by this store to inject the appropriate certificates and private + * keys before creating the certificate builders. + * + * @return A non-null reference. + * @since 0.1.0 + */ + CalypsoCertificateStore getCalypsoCertificateStore(); + + /** + * Creates a new generator for Calypso CA certificates (version 1) using an external signer. + * + *

The generator is used to configure and generate signed Calypso CA certificates. + * + *

This method must be called after the issuer certificate of the signer using the specified + * issuer public key reference. + * + * @param issuerPublicKeyReference The reference to issuer's public key in the store. + * @param caCertificateSigner The external signer to use for signing the CA certificate. + * @throws IllegalStateException If the provided reference is unknown. + * @throws CertificateConsistencyException If the provided reference doesn't designate a valid + * certification for the operation. + * @return A non-null reference. + * @since 0.1.0 + */ + CalypsoCaCertificateV1Generator createCalypsoCaCertificateV1Generator( + byte[] issuerPublicKeyReference, CalypsoCertificateSignerSpi caCertificateSigner); + + /** + * Creates a new generator for Calypso card certificates (version 1) using an external signer. + * + *

The generator is used to configure and generate signed Calypso card certificates. + * + *

This method must be called after the issuer certificate of the signer using the specified + * issuer public key reference. + * + * @param issuerPublicKeyReference The reference to issuer's public key in the store. + * @param cardCertificateSigner The external signer to use for signing the card certificate. + * @throws IllegalStateException If the provided reference is unknown. + * @throws CertificateConsistencyException If the provided reference doesn't designate a valid + * certification for the operation. + * @return A non-null reference. + * @since 0.1.0 + */ + CalypsoCardCertificateV1Generator createCalypsoCardCertificateV1Generator( + byte[] issuerPublicKeyReference, CalypsoCertificateSignerSpi cardCertificateSigner); +} diff --git a/src/main/java/org/eclipse/keypop/calypso/certificate/CalypsoCertificateApiProperties.java b/src/main/java/org/eclipse/keypop/calypso/certificate/CalypsoCertificateApiProperties.java new file mode 100644 index 0000000..44d67ce --- /dev/null +++ b/src/main/java/org/eclipse/keypop/calypso/certificate/CalypsoCertificateApiProperties.java @@ -0,0 +1,30 @@ +/* ************************************************************************************** + * Copyright (c) 2024 Calypso Networks Association https://calypsonet.org/ + * + * See the NOTICE file(s) distributed with this work for additional information + * regarding copyright ownership. + * + * This program and the accompanying materials are made available under the terms of the + * MIT License which is available at https://opensource.org/licenses/MIT + * + * SPDX-License-Identifier: MIT + ************************************************************************************** */ +package org.eclipse.keypop.calypso.certificate; + +/** + * API properties. + * + * @since 0.1.0 + */ +public final class CalypsoCertificateApiProperties { + + /** + * API version: {@value} + * + * @since 0.1.0 + */ + public static final String VERSION = "0.1"; + + /** Private constructor */ + private CalypsoCertificateApiProperties() {} +} diff --git a/src/main/java/org/eclipse/keypop/calypso/certificate/CalypsoCertificateStore.java b/src/main/java/org/eclipse/keypop/calypso/certificate/CalypsoCertificateStore.java new file mode 100644 index 0000000..2662cee --- /dev/null +++ b/src/main/java/org/eclipse/keypop/calypso/certificate/CalypsoCertificateStore.java @@ -0,0 +1,97 @@ +/* ************************************************************************************** + * Copyright (c) 2024 Calypso Networks Association https://calypsonet.org/ + * + * See the NOTICE file(s) distributed with this work for additional information + * regarding copyright ownership. + * + * This program and the accompanying materials are made available under the terms of the + * MIT License which is available at https://opensource.org/licenses/MIT + * + * SPDX-License-Identifier: MIT + ************************************************************************************** */ +package org.eclipse.keypop.calypso.certificate; + +import java.security.interfaces.RSAPublicKey; + +/** + * Provides a store for managing Calypso Certificate Authority (CA) certificates and public keys. + * + *

The stored certificates and keys are used for verifying and creating Calypso CA certificates + * within the certificate infrastructure. + * + *

Certificates and keys must be added to the store in a specific trust order to ensure a proper + * chain of trust within the certificate infrastructure. This order reflects the trust relationships + * between different authorities, allowing certificates to verify their validity up to a trusted + * root. + * + * @since 0.1.0 + */ +public interface CalypsoCertificateStore { + + /** + * Adds a Primary Certification Authority (PCA) public key and its reference. + * + *

This method expects a 2048-bit RSA public key with an exponent of 65537. + * + *

The provided public key reference will be used for identifying the key when creating + * certificates within the certificate infrastructure. + * + *

The key reference is to be used when creating certificates according to the desired + * infrastructure. + * + *

This method is suitable for providing means to verify CA certificates. + * + * @param pcaPublicKeyReference The reference to the PCA public key. + * @param pcaPublicKey The PCA public key. + * @throws IllegalArgumentException If one of the argument is null or if the key is not a 2048-bit + * RSA key. + * @throws IllegalStateException If the reference to the public key already in the store. + * @since 0.1.0 + */ + void addPcaPublicKey(byte[] pcaPublicKeyReference, RSAPublicKey pcaPublicKey); + + /** + * Adds a Primary Certification Authority (PCA) public key from it modulus and its reference. + * + *

This method expects the 256-byte modulus of a 2048-bit RSA public key with an exponent of + * 65537. + * + *

The provided public key reference will be used for identifying the key when creating + * certificates within the certificate infrastructure. + * + *

The key reference is to be used when creating certificates according to the desired + * infrastructure. + * + *

This method is suitable for providing means to verify CA certificates. + * + * @param pcaPublicKeyReference The reference to the PCA public key. + * @param pcaPublicKeyModulus The modulus of the PCA public key as a 256-byte byte array. + * @throws IllegalArgumentException If one of the argument is null or if the key modulus is not + * 256 bytes long. + * @throws IllegalStateException If the reference to the public key already in the store. + * @throws CertificateConsistencyException If the certificate is not trusted. + * @since 0.1.0 + */ + void addPcaPublicKey(byte[] pcaPublicKeyReference, byte[] pcaPublicKeyModulus); + + /** + * Adds a Calypso Certificate Authority (CA) certificate to the store and returns its public key + * reference. + * + *

This method adds the provided certificate to the store. The certificate must be valid + * (signed by an already referenced authority) and issued by a trusted authority previously + * referenced in the store. + * + *

This method is typically used to add an intermediate CA certificates necessary for + * validating other certificates within the infrastructure. + * + * @param caCertificate A 384-byte byte array containing the Calypso CA certificate to add. + * @return The public key reference of the added certificate. + * @throws IllegalArgumentException If the certificate is null or its format is unknown. + * @throws IllegalStateException If the reference to the public key already in the store or the + * parent certificate was not found. + * @throws CertificateConsistencyException If the certificate is not trusted. + * @since 0.1.0 + */ + byte[] addCalypsoCaCertificate(byte[] caCertificate); +} diff --git a/src/main/java/org/eclipse/keypop/calypso/certificate/CertificateConsistencyException.java b/src/main/java/org/eclipse/keypop/calypso/certificate/CertificateConsistencyException.java new file mode 100644 index 0000000..7ed8d8b --- /dev/null +++ b/src/main/java/org/eclipse/keypop/calypso/certificate/CertificateConsistencyException.java @@ -0,0 +1,39 @@ +/* ************************************************************************************** + * Copyright (c) 2024 Calypso Networks Association https://calypsonet.org/ + * + * See the NOTICE file(s) distributed with this work for additional information + * regarding copyright ownership. + * + * This program and the accompanying materials are made available under the terms of the + * MIT License which is available at https://opensource.org/licenses/MIT + * + * SPDX-License-Identifier: MIT + ************************************************************************************** */ +package org.eclipse.keypop.calypso.certificate; + +/** + * Indicated that an error occurred during the certificate validation process. + * + * @since 0.1.0 + */ +public class CertificateConsistencyException extends RuntimeException { + + /** + * @param message Message to identify the exception context. + * @since 0.1.0 + */ + public CertificateConsistencyException(String message) { + super(message); + } + + /** + * Encapsulates a lower level exception. + * + * @param message Message to identify the exception context. + * @param cause The cause. + * @since 0.1.0 + */ + public CertificateConsistencyException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/org/eclipse/keypop/calypso/certificate/CertificateSigningException.java b/src/main/java/org/eclipse/keypop/calypso/certificate/CertificateSigningException.java new file mode 100644 index 0000000..5b89143 --- /dev/null +++ b/src/main/java/org/eclipse/keypop/calypso/certificate/CertificateSigningException.java @@ -0,0 +1,39 @@ +/* ************************************************************************************** + * Copyright (c) 2024 Calypso Networks Association https://calypsonet.org/ + * + * See the NOTICE file(s) distributed with this work for additional information + * regarding copyright ownership. + * + * This program and the accompanying materials are made available under the terms of the + * MIT License which is available at https://opensource.org/licenses/MIT + * + * SPDX-License-Identifier: MIT + ************************************************************************************** */ +package org.eclipse.keypop.calypso.certificate; + +/** + * Indicates that an error occurred during the certificate signing process. + * + * @since 0.1.0 + */ +public class CertificateSigningException extends RuntimeException { + + /** + * @param message Message to identify the exception context. + * @since 0.1.0 + */ + public CertificateSigningException(String message) { + super(message); + } + + /** + * Encapsulates a lower level exception. + * + * @param message Message to identify the exception context. + * @param cause The cause. + * @since 0.1.0 + */ + public CertificateSigningException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/org/eclipse/keypop/calypso/certificate/package-info.java b/src/main/java/org/eclipse/keypop/calypso/certificate/package-info.java new file mode 100644 index 0000000..4abf674 --- /dev/null +++ b/src/main/java/org/eclipse/keypop/calypso/certificate/package-info.java @@ -0,0 +1,6 @@ +/** + * Interfaces related to Calypso certificates. + * + * @since 0.1.0 + */ +package org.eclipse.keypop.calypso.certificate; diff --git a/src/main/java/org/eclipse/keypop/calypso/certificate/spi/CalypsoCertificateSignerSpi.java b/src/main/java/org/eclipse/keypop/calypso/certificate/spi/CalypsoCertificateSignerSpi.java new file mode 100644 index 0000000..c3dea4a --- /dev/null +++ b/src/main/java/org/eclipse/keypop/calypso/certificate/spi/CalypsoCertificateSignerSpi.java @@ -0,0 +1,35 @@ +/* ************************************************************************************** + * Copyright (c) 2024 Calypso Networks Association https://calypsonet.org/ + * + * See the NOTICE file(s) distributed with this work for additional information + * regarding copyright ownership. + * + * This program and the accompanying materials are made available under the terms of the + * MIT License which is available at https://opensource.org/licenses/MIT + * + * SPDX-License-Identifier: MIT + ************************************************************************************** */ +package org.eclipse.keypop.calypso.certificate.spi; + +/** + * Signer for a Calypso certificate. + * + *

Implementations of this interface provide the cryptographic signing functionality used to + * generate signed Calypso CA and card certificates. + * + * @since 0.1.0 + */ +public interface CalypsoCertificateSignerSpi { + + /** + * Generates a signed card certificate based on the provided data and recoverable data. + * + * @param data The byte array containing the non-recoverable data for the certificate. + * @param recoverableData The byte array containing the recoverable data for the certificate. This + * might be encrypted or protected data that shouldn't be included in the final certificate + * but is needed for signature generation. + * @return The signed certificate, a byte array containing the data followed by the signature. + * @since 0.1.0 + */ + byte[] generateSignedCertificate(byte[] data, byte[] recoverableData); +} diff --git a/src/main/java/org/eclipse/keypop/calypso/certificate/spi/package-info.java b/src/main/java/org/eclipse/keypop/calypso/certificate/spi/package-info.java new file mode 100644 index 0000000..4babe86 --- /dev/null +++ b/src/main/java/org/eclipse/keypop/calypso/certificate/spi/package-info.java @@ -0,0 +1,6 @@ +/** + * SPIs to be implemented by end user applications related to Calypso certificates signing. + * + * @since 0.1.0 + */ +package org.eclipse.keypop.calypso.certificate.spi; diff --git a/src/main/javadoc/overview.html b/src/main/javadoc/overview.html new file mode 100644 index 0000000..7fc8efe --- /dev/null +++ b/src/main/javadoc/overview.html @@ -0,0 +1,15 @@ + + +

+ Welcome to the Java documentation of the Keypop Calypso Certificate Java API, + part of the Keypop open-source project hosted by the + Eclipse Foundation. +

+

+ Aligned with the Terminal Calypso Certificate UML specifications proposed by + the Calypso Networks Association (available + here), + it defines the generic interfaces required to create Calypso certificates. +

+ + \ No newline at end of file diff --git a/src/test/java/org/eclipse/keypop/calypso/certificate/CalypsoCertificateApiPropertiesTest.java b/src/test/java/org/eclipse/keypop/calypso/certificate/CalypsoCertificateApiPropertiesTest.java new file mode 100644 index 0000000..7c49cec --- /dev/null +++ b/src/test/java/org/eclipse/keypop/calypso/certificate/CalypsoCertificateApiPropertiesTest.java @@ -0,0 +1,44 @@ +/* ************************************************************************************** + * Copyright (c) 2024 Calypso Networks Association https://calypsonet.org/ + * + * See the NOTICE file(s) distributed with this work for additional information + * regarding copyright ownership. + * + * This program and the accompanying materials are made available under the terms of the + * MIT License which is available at https://opensource.org/licenses/MIT + * + * SPDX-License-Identifier: MIT + ************************************************************************************** */ +package org.eclipse.keypop.calypso.certificate; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.FileInputStream; +import java.io.InputStream; +import java.util.Properties; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class CalypsoCertificateApiPropertiesTest { + + private static String libVersion; + + @BeforeAll + public static void beforeClass() throws Exception { + InputStream inputStream = new FileInputStream("gradle.properties"); + try { + Properties properties = new Properties(); + properties.load(inputStream); + libVersion = properties.getProperty("version"); + } finally { + inputStream.close(); + } + } + + @Test + public void versionIsCorrectlyWritten() { + String apiVersion = CalypsoCertificateApiProperties.VERSION; + assertThat(apiVersion).matches("\\d+\\.\\d+"); + assertThat(libVersion).startsWith(apiVersion); + } +}