# SSCG - Simple Signed Certificate Generator

<a href="https://scan.coverity.com/projects/sscg">
  <img alt="Coverity Scan Build Status"
       src="https://scan.coverity.com/projects/12070/badge.svg"/>
</a>

## Why?
x509 Certificates are complicated to get right. SSCG makes it easy to generate usable, _signed_ certificates quickly without needing to understand complex `openssl`, `certtool` or `certutil` commands.

## License
sscg is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version, along with an exception permitting the code to link against the OpenSSL library.

## Quick Usage - Certificates Signed by a Private CA
```
/usr/bin/sscg
````
This will produce a `ca.crt` and a pair of `service.pem` and `service-key.pem` files in the current directory, the latter of which is signed by the private key of the `ca.crt`. The CA's private key will be destroyed by default after these files are created, so nothing further can be signed by it. (See [this blog post](https://sgallagh.wordpress.com/2016/05/02/self-signed-ssltls-certificates-why-they-are-terrible-and-a-better-alternative/)  for details on why this is desirable.)

Certificates generated by SSCG will be valid by default for securing the current machine's hostname and no others. To add further names (such as for a multi-homed system), look at the `--subject-alt-name` argument below.


## Full Usage
Usage of sscg:
```
Usage: sscg [OPTION...]
  -V, --version                                                Display the version number and exit.
  -f, --force                                                  Overwrite any pre-existing files in the requested locations

Help options:
  -?, --help                                                   Show this help message
      --usage                                                  Display brief usage message

Verbosity options
  -q, --quiet                                                  Display no output unless there is an error.
  -v, --verbose                                                Display progress messages.
  -d, --debug                                                  Enable logging of debug messages. Implies verbose. Warning! This will print private key information to
                                                               the screen!

Certificate Subject options
      --lifetime=1-3650                                        Certificate lifetime (days). (default: 398)
      --country=US, CZ, etc.                                   Certificate DN: Country (C). (default: "US")
      --state=Massachusetts, British Columbia, etc.            Certificate DN: State or Province (ST).
      --locality=Westford, Paris, etc.                         Certificate DN: Locality (L).
      --organization=My Company                                Certificate DN: Organization (O). (default: "Unspecified")
      --organizational-unit=Engineering, etc.                  Certificate DN: Organizational Unit (OU).
      --email=myname@example.com                               Certificate DN: Email Address (Email).
      --hostname=server.example.com                            The valid hostname of the certificate. Must be an FQDN. (default: current system FQDN)
      --subject-alt-name alt.example.com                       Optional additional valid hostnames for the certificate. In addition to hostnames, this option also
                                                               accepts explicit values supported by RFC 5280 such as IP:xxx.xxx.xxx.xxx May be specified multiple
                                                               times.

Certificate Key Cryptography Options
      --key-type={rsa,ecdsa,mldsa}                             Type of key to use for the certificate private keys. (default: rsa)
      --key-strength=2048 or larger                            Strength of the certificate private keys in bits. This argument is only valid if --key-type is set to
                                                               rsa. (default: 2048)
      --ec-curve={secp224r1,secp256r1,secp384r1,secp521r1}     EC curve to use for the certificate private keys. This argument is only valid if --key-type is set to
                                                               ecdsa.
      --mldsa-nist-level={2,3,5}                               NIST level to use for the ML-DSA key. This argument is only valid if --key-type is set to mldsa.
                                                               (default: 2)
      --hash-alg={sha256,sha384,sha512}                        Hashing algorithm to use for signing RSA and ECDSA keys. This argument is only valid if --key-type is
                                                               rsa or ecdsa. (default: "sha256")
      --cipher-alg={des-ede3-cbc,aes-256-cbc}                  Cipher to use for encrypting key files. (default: "aes-256-cbc")

Certificate Authority File Options
      --ca-file=STRING                                         Path where the public CA certificate will be stored. (default: "./ca.crt")
      --ca-mode=0644                                           File mode of the created CA certificate.
      --ca-key-file=STRING                                     Path where the CA's private key will be stored. If unspecified, the key will be destroyed rather than
                                                               written to the disk.
      --ca-key-mode=0600                                       File mode of the created CA key.
      --ca-key-password=STRING                                 Provide a password for the CA key file. Note that this will be visible in the process table for all
                                                               users, so it should be used for testing purposes only. Use --ca-keypassfile or --ca-key-password-prompt
                                                               for secure password entry.
      --ca-key-passfile=STRING                                 A file containing the password to encrypt the CA key file.
  -C, --ca-key-password-prompt                                 Prompt to enter a password for the CA key file.

Certificate Revocation List File Options
      --crl-file=STRING                                        Path where an (empty) Certificate Revocation List file will be created, for applications that expect
                                                               such a file to exist. If unspecified, no such file will be created.
      --crl-mode=0644                                          File mode of the created Certificate Revocation List.

Service Certificate File Options
      --cert-file=STRING                                       Path where the public service certificate will be stored. (default "./service.pem")
      --cert-mode=0644                                         File mode of the created certificate.
      --cert-key-file=STRING                                   Path where the service's private key will be stored. (default "service-key.pem")
      --cert-key-mode=0600                                     File mode of the created certificate key.
  -p, --cert-key-password=STRING                               Provide a password for the service key file. Note that this will be visible in the process table for
                                                               all users, so this flag should be used for testing purposes only. Use --cert-keypassfile or
                                                               --cert-key-password-prompt for secure password entry.
      --cert-key-passfile=STRING                               A file containing the password to encrypt the service key file.
  -P, --cert-key-password-prompt                               Prompt to enter a password for the service key file.

Client Authentication Certificate File Options
      --client-file=STRING                                     Path where a client authentication certificate will be stored.
      --client-mode=0644                                       File mode of the created certificate.
      --client-key-file=STRING                                 Path where the client's private key will be stored. (default is the client-file)
      --client-key-mode=0600                                   File mode of the created certificate key.
      --client-key-password=STRING                             Provide a password for the client key file. Note that this will be visible in the process table for all
                                                               users, so this flag should be used for testing purposes only. Use --client-keypassfile or
                                                               --client-key-password-prompt for secure password entry.
      --client-key-passfile=STRING                             A file containing the password to encrypt the client key file.
      --client-key-password-prompt                             Prompt to enter a password for the client key file.

Diffie-Hellman Parameter File Options
      --dhparams-file=STRING                                   A file to contain a set of Diffie-Hellman parameters. (Default: not created)
      --dhparams-named-group=STRING                            Output well-known DH parameters. The available named groups are: ffdhe2048, ffdhe3072, ffdhe4096,
                                                               ffdhe6144, ffdhe8192, modp_2048, modp_3072, modp_4096, modp_6144, modp_8192, modp_1536, dh_1024_160,
                                                               dh_2048_224, dh_2048_256. (Default: "ffdhe4096")
      --dhparams-generator={2,3,5}                             The generator value for dhparams. (default: 2)
```


## Development Setup and Contribution Guide

### Building from Source

#### Dependencies

**Build dependencies:**
```bash
# Fedora/RHEL/CentOS
dnf install meson ninja-build gcc pkgconf-pkg-config
dnf install openssl-devel talloc-devel popt-devel
dnf install gettext-devel help2man clang-format

# Debian/Ubuntu
apt-get install meson ninja-build gcc pkg-config
apt-get install libssl-dev libtalloc-dev libpopt-dev
apt-get install gettext help2man clang-format
```

**Runtime dependencies:**
- OpenSSL 3.0.0+ (libssl, libcrypto)
- talloc
- popt

#### Build Process

1. **Clone the repository:**
   ```bash
   git clone https://github.com/sgallagher/sscg.git
   cd sscg
   ```

2. **Configure the build:**
   ```bash
   meson setup build
   ```

3. **Build the project:**
   ```bash
   ninja -C build
   ```

4. **Run tests:**
   ```bash
   ninja -C build test
   ```

5. **Install (optional):**
   ```bash
   ninja -C build install
   ```

#### Development Build Options

**Debug build with additional checks:**
```bash
meson setup build --buildtype=debug
ninja -C build
```

**Release build:**
```bash
meson setup build --buildtype=release
ninja -C build
```

**Run slow tests (including DH parameter generation):**
```bash
meson setup build -Drun_slow_tests=true
ninja -C build test
```

### Development Workflow

#### Code Style and Formatting

SSCG uses `clang-format` for consistent code formatting:

**Auto-format code:**
   ```bash
   clang-format -i src/*.c include/*.h test/*.c
   ```

#### Running Tests

**Basic test suite:**
```bash
ninja -C build test
```

**Individual tests:**
```bash
# Run specific test
./build/create_cert_test
./build/create_ca_test
./build/init_bignum_test
```

**Memory leak detection:**
```bash
SSCG_TALLOC_REPORT=true ./build/sscg
```

#### Code Architecture

**Key components:**
- `src/sscg.c` - Main application entry point
- `src/arguments.c` - Command-line argument parsing
- `src/authority.c` - Certificate Authority management
- `src/cert.c` - Certificate creation and signing
- `src/x509.c` - X.509 certificate operations
- `src/key.c` - Key generation and management
- `src/io_utils.c` - File I/O operations
- `src/dhparams.c` - Diffie-Hellman parameter generation

**Memory management:**
- All dynamic memory uses [talloc](https://talloc.samba.org/talloc/doc/html/index.html)
- Use `TALLOC_CTX` for hierarchical cleanup
- Follow talloc parent-child relationships
- Never use `malloc()`/`free()` directly

**Error handling:**
- Use `CHECK_OK()` macro for error propagation
- Return appropriate errno values
- Clean up resources on error paths
- Provide meaningful error messages to users

### Contributing

#### Before Submitting

1. **Code quality checks:**
   ```bash
   ninja -C build test
   ```

2. **Test on multiple platforms** if possible (Fedora, Ubuntu, etc.)

3. **Update translations** if you added user-facing strings:
   ```bash
   ninja -C build sscg-pot
   ninja -C build sscg-update-po
   ```

#### Submitting Changes

1. **Create descriptive commit messages:**
   ```
   Add support for ECDSA key generation
   
   - Implement ECDSA key creation in key.c
   - Add curve selection via --key-curve option
   - Update tests for ECDSA functionality
   - Update documentation
   ```

2. **Submit pull request** with:
   - Clear description of changes
   - Rationale for the change
   - Test results
   - Any breaking changes noted

#### Bug Reports

When reporting bugs, include:
- SSCG version (`sscg --version`)
- Operating system and version
- OpenSSL version
- Complete command line used
- Expected vs. actual behavior
- Any error messages

#### Security Issues

For security-related issues, please contact the [maintainer](mailto:sgallagh@redhat.com) directly rather than filing a public issue.

### Debugging

#### Common Development Tools

**GDB debugging:**
```bash
gdb --args ./build/sscg --debug
```

**Valgrind for memory checking:**
```bash
valgrind --leak-check=full --show-leak-kinds=all ./build/sscg
```

#### Troubleshooting Build Issues

**Meson configuration issues:**
```bash
# Clean and reconfigure
rm -rf build
meson setup build
```

**Missing dependencies:**
```bash
# Check what's missing
meson setup build 2>&1 | grep -i "not found\|missing"
```

**Test failures:**
```bash
# Run tests with verbose output
meson test -C build -v
```

## Internationalization (i18n) and Translations

SSCG supports internationalization and can display messages in multiple languages.

### For Users

To use SSCG in your preferred language, set the `LANG` environment variable:

```bash
LANG=es_ES.UTF-8 sscg --help    # Spanish
LANG=fr_FR.UTF-8 sscg --help    # French
LANG=de_DE.UTF-8 sscg --help    # German
```

If a translation is not available for your language, SSCG will fall back to English.

### For Translators

To create or update translations:

1. **Install required tools:**
   ```bash
   # Fedora/RHEL/CentOS
   dnf install gettext-devel meson ninja-build

   # Debian/Ubuntu
   apt-get install gettext meson ninja-build
   ```

2. **Set up the build environment:**
   ```bash
   meson setup build
   cd build
   ```

3. **Generate/update the translation template:**
   ```bash
   ninja sscg-pot
   ```

4. **Create a new translation (replace `LANG` with your language code):**
   ```bash
   msginit -l LANG -i po/sscg.pot -o po/LANG.po
   ```

5. **Update an existing translation:**
   ```bash
   msgmerge -U po/LANG.po po/sscg.pot
   ```

6. **Edit the translation file:**
   ```bash
   # Use your preferred editor or a specialized tool like poedit
   vim po/LANG.po
   ```

7. **Test your translation:**
   ```bash
   ninja
   LANG=LANG.UTF-8 ./sscg --help
   ```

8. **Submit your translation:** Create a pull request with your `.po` file.

### For Developers

When adding new user-facing strings to the code:

1. **Mark strings for translation** by wrapping them with `_()`:
   ```c
   fprintf(stderr, _("Error: Could not open file\n"));
   ```

2. **Update the translation template** after adding new strings:
   ```bash
   ninja sscg-pot
   ```

3. **Update existing translations:**
   ```bash
   ninja sscg-update-po
   ```

4. **Test with different locales** to ensure proper functionality.

**Important:** Do not internationalize:
- Debug messages (use `fprintf(stderr, "DEBUG: ...")` without `_()`)
- Technical error codes or OpenSSL error strings
- Test files and test output

### Supported Languages

Current translations are maintained in the `po/` directory. To see available languages:
```bash
ls po/*.po
```

### Translation Status

To check the completion status of translations:
```bash
msgfmt --statistics po/LANG.po
```
