Compare commits

...

7 commits

Author SHA1 Message Date
Daniel
4c4b4471d8 Add support for json file signing 2024-11-08 14:28:09 +01:00
Daniel
4fbce7d649 Switch from portbase to structures lib, update build and version system 2024-06-24 09:48:48 +02:00
Daniel
393206b5e2 Bump version 2023-12-19 15:48:11 +01:00
Daniel
c475557cd1 Update deps 2023-12-19 15:48:05 +01:00
Daniel Hovie
2a3994208b
Merge pull request from safing/feature/blake3
Add support for BLAKE3, add Suites with BLAKE3
2023-12-19 15:42:34 +01:00
Daniel
a93f8142fc Remove CodeQL workflow 2023-12-18 20:28:07 +01:00
Daniel
914459cb99 Add support for BLAKE3, add Suites with BLAKE3 2023-11-22 14:37:23 +01:00
40 changed files with 828 additions and 340 deletions

View file

@ -1,72 +0,0 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ "master" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "master" ]
schedule:
- cron: '38 16 * * 3'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'go' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2

View file

@ -1,61 +1,29 @@
#!/bin/bash
set -eo pipefail
baseDir="$( cd "$(dirname "$0")" && pwd )"
cd "$baseDir"
# get build data
if [[ "$BUILD_COMMIT" == "" ]]; then
BUILD_COMMIT=$(git describe --all --long --abbrev=99 --dirty 2>/dev/null)
fi
if [[ "$BUILD_USER" == "" ]]; then
BUILD_USER=$(id -un)
fi
if [[ "$BUILD_HOST" == "" ]]; then
BUILD_HOST=$(hostname)
fi
if [[ "$BUILD_DATE" == "" ]]; then
BUILD_DATE=$(date +%d.%m.%Y)
fi
if [[ "$BUILD_SOURCE" == "" ]]; then
BUILD_SOURCE=$(git remote -v | grep origin | cut -f2 | cut -d" " -f1 | head -n 1)
fi
if [[ "$BUILD_SOURCE" == "" ]]; then
BUILD_SOURCE=$(git remote -v | cut -f2 | cut -d" " -f1 | head -n 1)
fi
BUILD_BUILDOPTIONS=$(echo $* | sed "s/ /§/g")
# check
if [[ "$BUILD_COMMIT" == "" ]]; then
echo "could not automatically determine BUILD_COMMIT, please supply manually as environment variable."
exit 1
fi
if [[ "$BUILD_USER" == "" ]]; then
echo "could not automatically determine BUILD_USER, please supply manually as environment variable."
exit 1
fi
if [[ "$BUILD_HOST" == "" ]]; then
echo "could not automatically determine BUILD_HOST, please supply manually as environment variable."
exit 1
fi
if [[ "$BUILD_DATE" == "" ]]; then
echo "could not automatically determine BUILD_DATE, please supply manually as environment variable."
exit 1
fi
if [[ "$BUILD_SOURCE" == "" ]]; then
echo "could not automatically determine BUILD_SOURCE, please supply manually as environment variable."
exit 1
fi
echo "Please notice, that this build script includes metadata into the build."
echo "This information is useful for debugging and license compliance."
echo "Run the compiled binary with the version command to see the information included."
# Get version.
VERSION="$(git tag --points-at)" || true
test -z "$VERSION" && DEV_VERSION="$(git describe --tags --first-parent --abbrev=0)" || true
test -n "$DEV_VERSION" && VERSION="${DEV_VERSION}_dev_build"
test -z "$VERSION" && VERSION="dev_build"
BUILD_SOURCE=$( ( git remote -v | cut -f2 | cut -d" " -f1 | head -n 1 ) || echo "unknown" )
BUILD_TIME=$(date -u "+%Y-%m-%dT%H:%M:%SZ" || echo "unknown")
LDFLAGS="-X main.Version=${VERSION} -X main.BuildSource=${BUILD_SOURCE} -X main.BuildTime=${BUILD_TIME}"
# build output name
BIN_NAME="jess"
if [[ "$GOOS" == "windows" ]]; then
BIN_NAME="${BIN_NAME}.exe"
fi
# build
BUILD_PATH="github.com/safing/portbase/info"
go build -o "${BIN_NAME}" -ldflags "-X ${BUILD_PATH}.commit=${BUILD_COMMIT} -X ${BUILD_PATH}.buildOptions=${BUILD_BUILDOPTIONS} -X ${BUILD_PATH}.buildUser=${BUILD_USER} -X ${BUILD_PATH}.buildHost=${BUILD_HOST} -X ${BUILD_PATH}.buildDate=${BUILD_DATE} -X ${BUILD_PATH}.buildSource=${BUILD_SOURCE}" "$@"
# Build.
export CGO_ENABLED=0
go build -o "${BIN_NAME}" -ldflags "$LDFLAGS" "$@"

View file

@ -11,7 +11,7 @@ import (
"github.com/spf13/cobra"
"github.com/safing/jess"
"github.com/safing/portbase/container"
"github.com/safing/structures/container"
)
func init() {

View file

@ -13,7 +13,7 @@ import (
"github.com/safing/jess"
"github.com/safing/jess/filesig"
"github.com/safing/portbase/container"
"github.com/safing/structures/container"
)
func init() {

View file

@ -2,20 +2,85 @@ package main
import (
"fmt"
"runtime"
"runtime/debug"
"strings"
"github.com/spf13/cobra"
"github.com/safing/portbase/info"
)
func init() {
rootCmd.AddCommand(versionCmd)
}
var versionCmd = &cobra.Command{
Use: "version",
Short: "print version information",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(info.FullVersion())
},
var (
// Version is the version of this command.
Version = "dev build"
// BuildSource holds the primary source repo used to build.
BuildSource = "unknown"
// BuildTime holds the time when the binary was built.
BuildTime = "unknown"
)
func init() {
// Convert version string space placeholders.
Version = strings.ReplaceAll(Version, "_", " ")
BuildSource = strings.ReplaceAll(BuildSource, "_", " ")
BuildTime = strings.ReplaceAll(BuildTime, "_", " ")
// Get build info.
buildInfo, _ := debug.ReadBuildInfo()
buildSettings := make(map[string]string)
for _, setting := range buildInfo.Settings {
buildSettings[setting.Key] = setting.Value
}
// Add "dev build" to version if repo is dirty.
if buildSettings["vcs.modified"] == "true" &&
!strings.HasSuffix(Version, "dev build") {
Version += " dev build"
}
rootCmd.AddCommand(versionCmd)
}
var versionCmd = &cobra.Command{
Use: "version",
Run: version,
}
func version(cmd *cobra.Command, args []string) {
builder := new(strings.Builder)
// Get build info.
buildInfo, _ := debug.ReadBuildInfo()
buildSettings := make(map[string]string)
for _, setting := range buildInfo.Settings {
buildSettings[setting.Key] = setting.Value
}
// Print version info.
builder.WriteString(fmt.Sprintf("Jess %s\n", Version))
// Build info.
cgoInfo := "-cgo"
if buildSettings["CGO_ENABLED"] == "1" {
cgoInfo = "+cgo"
}
builder.WriteString(fmt.Sprintf("\nbuilt with %s (%s %s) for %s/%s\n", runtime.Version(), runtime.Compiler, cgoInfo, runtime.GOOS, runtime.GOARCH))
builder.WriteString(fmt.Sprintf(" at %s\n", BuildTime))
// Commit info.
dirtyInfo := "clean"
if buildSettings["vcs.modified"] == "true" {
dirtyInfo = "dirty"
}
builder.WriteString(fmt.Sprintf("\ncommit %s (%s)\n", buildSettings["vcs.revision"], dirtyInfo))
builder.WriteString(fmt.Sprintf(" at %s\n", buildSettings["vcs.time"]))
builder.WriteString(fmt.Sprintf(" from %s\n", BuildSource))
// License info.
builder.WriteString("\nLicensed under the GPLv3 license.")
_, _ = fmt.Println(builder.String())
}

View file

@ -2,7 +2,6 @@ package main
import (
"errors"
"fmt"
"os"
"github.com/spf13/cobra"
@ -10,7 +9,6 @@ import (
"github.com/safing/jess"
_ "github.com/safing/jess/tools/all"
"github.com/safing/jess/truststores"
"github.com/safing/portbase/info"
)
const (
@ -41,14 +39,6 @@ var (
)
func main() {
info.Set("jess", "0.3.2", "GPLv3", true)
err := info.CheckVersion()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
rootCmd.PersistentFlags().StringVarP(&trustStoreDir, "tsdir", "d", "",
"specify a truststore directory (default loaded from JESS_TS_DIR env variable)",
)
@ -62,8 +52,7 @@ func main() {
rootCmd.PersistentFlags().IntVarP(&minimumSecurityLevel, "seclevel", "s", 0, "specify a minimum security level")
rootCmd.PersistentFlags().IntVarP(&defaultSymmetricKeySize, "symkeysize", "k", 0, "specify a default symmetric key size (only applies in certain conditions, use when prompted)")
err = rootCmd.Execute()
if err != nil {
if rootCmd.Execute() != nil {
os.Exit(1)
}
os.Exit(0)

View file

@ -5,7 +5,7 @@ import (
"testing"
"time"
"github.com/safing/portbase/container"
"github.com/safing/structures/container"
)
func TestWire(t *testing.T) {

View file

@ -5,7 +5,7 @@ import (
"errors"
"fmt"
"github.com/safing/portbase/container"
"github.com/safing/structures/container"
)
// Close encrypts (and possibly signs) the given data and returns a Letter. Storyline: Close takes an envelope, inserts the message and closes it, resulting in a letter.

View file

@ -6,7 +6,7 @@ import (
"github.com/mr-tron/base58"
"github.com/safing/portbase/formats/dsd"
"github.com/safing/structures/dsd"
)
// Envelope holds configuration for jess to put data into a letter.

View file

@ -7,7 +7,7 @@ import (
"regexp"
"github.com/safing/jess"
"github.com/safing/portbase/formats/dsd"
"github.com/safing/structures/dsd"
)
const (

View file

@ -1,6 +1,7 @@
package filesig
import (
"encoding/base64"
"errors"
"fmt"
@ -9,7 +10,9 @@ import (
"github.com/tidwall/sjson"
"golang.org/x/exp/slices"
"github.com/safing/jess"
"github.com/safing/jess/lhash"
"github.com/safing/structures/dsd"
)
// JSON file metadata keys.
@ -32,10 +35,10 @@ func AddJSONChecksum(data []byte) ([]byte, error) {
checksums = append(checksums, h.Base58())
// Sort and deduplicate checksums and sigs.
slices.Sort[[]string, string](checksums)
checksums = slices.Compact[[]string, string](checksums)
slices.Sort[[]string, string](signatures)
signatures = slices.Compact[[]string, string](signatures)
slices.Sort(checksums)
checksums = slices.Compact(checksums)
slices.Sort(signatures)
signatures = slices.Compact(signatures)
// Add metadata and return.
return jsonAddMeta(content, checksums, signatures)
@ -72,6 +75,86 @@ func VerifyJSONChecksum(data []byte) error {
return nil
}
func AddJSONSignature(data []byte, envelope *jess.Envelope, trustStore jess.TrustStore) (signedData []byte, err error) {
// Create session.
session, err := envelope.Correspondence(trustStore)
if err != nil {
return nil, fmt.Errorf("invalid signing envelope: %w", err)
}
// Check if the envelope is suitable for signing.
if err := envelope.Suite().Provides.CheckComplianceTo(fileSigRequirements); err != nil {
return nil, fmt.Errorf("envelope not suitable for signing: %w", err)
}
// Extract content and metadata from json.
content, checksums, signatures, err := jsonSplit(data)
if err != nil {
return nil, fmt.Errorf("invalid json structure: %w", err)
}
// Sign data.
letter, err := session.Close(content)
if err != nil {
return nil, fmt.Errorf("sign: %w", err)
}
// Serialize signature and add it.
letter.Data = nil
sig, err := letter.ToDSD(dsd.CBOR)
if err != nil {
return nil, fmt.Errorf("serialize sig: %w", err)
}
signatures = append(signatures, base64.RawURLEncoding.EncodeToString(sig))
// Sort and deduplicate checksums and sigs.
slices.Sort(checksums)
checksums = slices.Compact(checksums)
slices.Sort(signatures)
signatures = slices.Compact(signatures)
// Add metadata and return.
return jsonAddMeta(data, checksums, signatures)
}
func VerifyJSONSignature(data []byte, trustStore jess.TrustStore) (err error) {
// Extract content and metadata from json.
content, _, signatures, err := jsonSplit(data)
if err != nil {
return fmt.Errorf("invalid json structure: %w", err)
}
var signaturesVerified int
for i, sig := range signatures {
// Deserialize signature.
sigData, err := base64.RawURLEncoding.DecodeString(sig)
if err != nil {
return fmt.Errorf("signature %d malformed: %w", i+1, err)
}
letter := &jess.Letter{}
_, err = dsd.Load(sigData, letter)
if err != nil {
return fmt.Errorf("signature %d malformed: %w", i+1, err)
}
// Verify signature.
letter.Data = content
err = letter.Verify(fileSigRequirements, trustStore)
if err != nil {
return fmt.Errorf("signature %d invalid: %w", i+1, err)
}
signaturesVerified++
}
// Fail when no signatures were verified.
if signaturesVerified == 0 {
return ErrSignatureMissing
}
return nil
}
func jsonSplit(data []byte) (
content []byte,
checksums []string,
@ -187,10 +270,9 @@ func jsonAddMeta(data []byte, checksums, signatures []string) ([]byte, error) {
// Final pretty print.
data = pretty.PrettyOptions(data, &pretty.Options{
Width: 200, // Must not change!
Prefix: "", // Must not change!
Indent: " ", // Must not change!
SortKeys: true, // Must not change!
Width: 200, // Must not change!
Prefix: "", // Must not change!
Indent: " ", // Must not change!
})
return data, nil

View file

@ -4,6 +4,10 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/safing/jess"
"github.com/safing/jess/tools"
)
func TestJSONChecksums(t *testing.T) {
@ -22,9 +26,9 @@ func TestJSONChecksums(t *testing.T) {
`
testJSONWithChecksum, err := AddJSONChecksum([]byte(json))
assert.NoError(t, err, "should be able to add checksum")
require.NoError(t, err, "should be able to add checksum")
assert.Equal(t, jsonWithChecksum, string(testJSONWithChecksum), "should match")
assert.NoError(t,
require.NoError(t,
VerifyJSONChecksum(testJSONWithChecksum),
"checksum should be correct",
)
@ -33,7 +37,7 @@ func TestJSONChecksums(t *testing.T) {
"c": 1, "a":"b",
"_jess-checksum": "ZwtAd75qvioh6uf1NAq64KRgTbqeehFVYmhLmrwu1s7xJo"
}`
assert.NoError(t,
require.NoError(t,
VerifyJSONChecksum([]byte(jsonWithChecksum)),
"checksum should be correct",
)
@ -48,7 +52,7 @@ func TestJSONChecksums(t *testing.T) {
"c": 1
}
`
assert.NoError(t,
require.NoError(t,
VerifyJSONChecksum([]byte(jsonWithMultiChecksum)),
"checksum should be correct",
)
@ -61,9 +65,9 @@ func TestJSONChecksums(t *testing.T) {
`
testJSONWithMultiChecksum, err := AddJSONChecksum([]byte(jsonWithMultiChecksum))
assert.NoError(t, err, "should be able to add checksum")
require.NoError(t, err, "should be able to add checksum")
assert.Equal(t, jsonWithMultiChecksumOutput, string(testJSONWithMultiChecksum), "should match")
assert.NoError(t,
require.NoError(t,
VerifyJSONChecksum(testJSONWithMultiChecksum),
"checksum should be correct",
)
@ -117,3 +121,106 @@ func TestJSONChecksums(t *testing.T) {
//
// assert.Error(t, VerifyTextFileChecksum([]byte(textWithFailingChecksums), "#"), "should fail")
}
func TestJSONSignatures(t *testing.T) {
t.Parallel()
// Get tool for key generation.
tool, err := tools.Get("Ed25519")
if err != nil {
t.Fatal(err)
}
// Generate key pair.
s, err := getOrMakeSignet(t, tool.StaticLogic, false, "test-key-jsonsig-1")
if err != nil {
t.Fatal(err)
}
// sBackup, err := s.Backup(true)
// if err != nil {
// t.Fatal(err)
// }
// t.Logf("signet: %s", sBackup)
// Make envelope.
envelope := jess.NewUnconfiguredEnvelope()
envelope.SuiteID = jess.SuiteSignV1
envelope.Senders = []*jess.Signet{s}
// Test 1: Simple json.
json := `{"a": "b", "c": 1}`
testJSONWithSignature, err := AddJSONSignature([]byte(json), envelope, testTrustStore)
require.NoError(t, err, "should be able to add signature")
require.NoError(t,
VerifyJSONSignature(testJSONWithSignature, testTrustStore),
"signature should be valid",
)
// Test 2: Prepared json with signature.
// Load signing key into trust store.
signingKey2, err := jess.SenderFromTextFormat(
"sender:2ZxXzzL3mc3mLPizTUe49zi8Z3NMbDrmmqJ4V9mL4AxefZ1o8pM8wPMuK2uW12Mvd3EJL9wsKTn14BDuqH2AtucvHTAkjDdZZ5YA9Azmji5tLRXmypvSxEj2mxXU3MFXBVdpzPdwRcE4WauLo9ZfQWebznvnatVLwuxmeo17tU2pL7",
)
if err != nil {
t.Fatal(err)
}
rcptKey2, err := signingKey2.AsRecipient()
if err != nil {
t.Fatal(err)
}
if err := testTrustStore.StoreSignet(rcptKey2); err != nil {
t.Fatal(err)
}
// Verify data.
jsonWithSignature := `{
"c":1,"a":"b",
"_jess-signature": "Q6RnVmVyc2lvbgFnU3VpdGVJRGdzaWduX3YxZU5vbmNlRK6e7JhqU2lnbmF0dXJlc4GjZlNjaGVtZWdFZDI1NTE5YklEeBl0ZXN0LXN0YXRpYy1rZXktanNvbnNpZy0xZVZhbHVlWEBPEbeM4_CTl3OhNT2z74h38jIZG5R7BBLDFd6npJ3E-4JqM6TaSMa-2pPEBf3fDNuikR3ak45SekC6Z10uWiEB"
}`
require.NoError(t,
VerifyJSONSignature([]byte(jsonWithSignature), testTrustStore),
"signature should be valid",
)
// Test 3: Add signature to prepared json.
testJSONWithSignature, err = AddJSONSignature([]byte(jsonWithSignature), envelope, testTrustStore)
require.NoError(t, err, "should be able to add signature")
require.NoError(t,
VerifyJSONSignature(testJSONWithSignature, testTrustStore),
"signatures should be valid",
)
// Test 4: Prepared json with multiple signatures.
// Load signing key into trust store.
signingKey3, err := jess.SenderFromTextFormat(
"sender:2ZxXzzL3mc3mLPizTUe49zi8Z3NMbDrmmqJ4V9mL4AxefZ1o8pM8wPMuRAXdZNaPX3B96bhGCpww6TbXJ6WXLHoLwLV196cgdm1BurfTMdjUPa4PUj1KgHuM82b1p8ezQeryzj1CsjeM8KRQdh9YP87gwKpXNmLW5GmUyWG5KxzZ7W",
)
if err != nil {
t.Fatal(err)
}
rcptKey3, err := signingKey3.AsRecipient()
if err != nil {
t.Fatal(err)
}
if err := testTrustStore.StoreSignet(rcptKey3); err != nil {
t.Fatal(err)
}
jsonWithMultiSig := `{
"_jess-signature": [
"Q6RnVmVyc2lvbgFnU3VpdGVJRGdzaWduX3YxZU5vbmNlRK6e7JhqU2lnbmF0dXJlc4GjZlNjaGVtZWdFZDI1NTE5YklEeBl0ZXN0LXN0YXRpYy1rZXktanNvbnNpZy0xZVZhbHVlWEBPEbeM4_CTl3OhNT2z74h38jIZG5R7BBLDFd6npJ3E-4JqM6TaSMa-2pPEBf3fDNuikR3ak45SekC6Z10uWiEB",
"Q6RnVmVyc2lvbgFnU3VpdGVJRGdzaWduX3YxZU5vbmNlRC32oylqU2lnbmF0dXJlc4GjZlNjaGVtZWdFZDI1NTE5YklEeBl0ZXN0LXN0YXRpYy1rZXktanNvbnNpZy0yZVZhbHVlWEDYVHeKaJvzZPOkgC6Tie6x70bNm2jtmJmAwDFDcBL1ddK7pVSefyAPg47xMO7jeucP5bw754P6CdrR5gyANJkM"
],
"a": "b",
"c": 1
}
`
assert.NoError(t,
VerifyJSONSignature([]byte(jsonWithMultiSig), testTrustStore),
"signatures should be valid",
)
}

View file

@ -6,7 +6,7 @@ import (
"github.com/safing/jess"
"github.com/safing/jess/lhash"
"github.com/safing/portbase/formats/dsd"
"github.com/safing/structures/dsd"
)
// Extension holds the default file extension to be used for signature files.
@ -53,7 +53,7 @@ func SignFileData(fileHash *lhash.LabeledHash, metaData map[string]string, envel
// Check if the envelope is suitable for signing.
if err := envelope.Suite().Provides.CheckComplianceTo(fileSigRequirements); err != nil {
return nil, nil, fmt.Errorf("envelope not suitable for signing")
return nil, nil, fmt.Errorf("envelope not suitable for signing: %w", err)
}
// Create struct and transform data into serializable format to be signed.

View file

@ -4,6 +4,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestTextChecksums(t *testing.T) {
@ -29,20 +30,20 @@ do_something()
`
testTextWithChecksumAfterComment, err := AddTextFileChecksum([]byte(text), "#", TextPlacementAfterComment)
assert.NoError(t, err, "should be able to add checksum")
require.NoError(t, err, "should be able to add checksum")
assert.Equal(t, textWithChecksumAfterComment, string(testTextWithChecksumAfterComment), "should match")
assert.NoError(t,
require.NoError(t,
VerifyTextFileChecksum(testTextWithChecksumAfterComment, "#"),
"checksum should be correct",
)
assert.NoError(t,
require.NoError(t,
VerifyTextFileChecksum(append(
[]byte("\n\n \r\n"),
testTextWithChecksumAfterComment...,
), "#"),
"checksum should be correct",
)
assert.NoError(t,
require.NoError(t,
VerifyTextFileChecksum(append(
testTextWithChecksumAfterComment,
[]byte("\r\n \n \n")...,
@ -62,9 +63,9 @@ do_something()
`
testTextWithChecksumAtTop, err := AddTextFileChecksum([]byte(text), "#", TextPlacementTop)
assert.NoError(t, err, "should be able to add checksum")
require.NoError(t, err, "should be able to add checksum")
assert.Equal(t, textWithChecksumAtTop, string(testTextWithChecksumAtTop), "should match")
assert.NoError(t,
require.NoError(t,
VerifyTextFileChecksum(testTextWithChecksumAtTop, "#"),
"checksum should be correct",
)
@ -82,9 +83,9 @@ do_something()
`
testTextWithChecksumAtBottom, err := AddTextFileChecksum([]byte(text), "#", TextPlacementBottom)
assert.NoError(t, err, "should be able to add checksum")
require.NoError(t, err, "should be able to add checksum")
assert.Equal(t, textWithChecksumAtBottom, string(testTextWithChecksumAtBottom), "should match")
assert.NoError(t,
require.NoError(t,
VerifyTextFileChecksum(testTextWithChecksumAtBottom, "#"),
"checksum should be correct",
)
@ -119,7 +120,7 @@ do_something()
do_something()
`
testTextWithMultiChecksumOutput, err := AddTextFileChecksum([]byte(textWithMultiChecksum), "#", TextPlacementAfterComment)
assert.NoError(t, err, "should be able to add checksum")
require.NoError(t, err, "should be able to add checksum")
assert.Equal(t, textWithMultiChecksumOutput, string(testTextWithMultiChecksumOutput), "should match")
// Test failing checksums.
@ -135,7 +136,7 @@ do_something()
do_something()
`
assert.Error(t, VerifyTextFileChecksum([]byte(textWithFailingChecksums), "#"), "should fail")
require.Error(t, VerifyTextFileChecksum([]byte(textWithFailingChecksums), "#"), "should fail")
}
func TestLineEndDetection(t *testing.T) {

34
go.mod
View file

@ -1,46 +1,50 @@
module github.com/safing/jess
go 1.20
go 1.21.1
toolchain go1.22.3
require (
github.com/AlecAivazis/survey/v2 v2.3.7
github.com/aead/ecdh v0.2.0
github.com/mr-tron/base58 v1.2.0
github.com/safing/portbase v0.18.5
github.com/safing/structures v1.1.0
github.com/satori/go.uuid v1.2.0
github.com/spf13/cobra v1.7.0
github.com/stretchr/testify v1.8.1
github.com/spf13/cobra v1.8.1
github.com/stretchr/testify v1.8.4
github.com/tevino/abool v1.2.0
github.com/tidwall/gjson v1.17.0
github.com/tidwall/gjson v1.17.1
github.com/tidwall/pretty v1.2.1
github.com/tidwall/sjson v1.2.5
github.com/zalando/go-keyring v0.2.3
golang.org/x/crypto v0.14.0
golang.org/x/exp v0.0.0-20231006140011-7918f672742d
github.com/zalando/go-keyring v0.2.5
github.com/zeebo/blake3 v0.2.3
golang.org/x/crypto v0.24.0
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8
)
require (
github.com/alessio/shellescape v1.4.2 // indirect
github.com/danieljoos/wincred v1.2.0 // indirect
github.com/danieljoos/wincred v1.2.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fxamacker/cbor/v2 v2.5.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/gofrs/uuid v4.4.0+incompatible // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/vmihailenco/msgpack/v5 v5.4.0 // indirect
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/term v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/term v0.21.0 // indirect
golang.org/x/text v0.16.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

77
go.sum
View file

@ -6,28 +6,34 @@ github.com/aead/ecdh v0.2.0 h1:pYop54xVaq/CEREFEcukHRZfTdjiWvYIsZDXXrBapQQ=
github.com/aead/ecdh v0.2.0/go.mod h1:a9HHtXuSo8J1Js1MwLQx2mBhkXMT6YwUmVVEY4tTB8U=
github.com/alessio/shellescape v1.4.2 h1:MHPfaU+ddJ0/bYWpgIeUnQUqKrlJ1S7BfEYPM4uEoM0=
github.com/alessio/shellescape v1.4.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/danieljoos/wincred v1.2.0 h1:ozqKHaLK0W/ii4KVbbvluM91W2H3Sh0BncbUNPS7jLE=
github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec=
github.com/danieljoos/wincred v1.2.1 h1:dl9cBrupW8+r5250DYkYxocLeZ1Y4vB1kxgtjxw8GQs=
github.com/danieljoos/wincred v1.2.1/go.mod h1:uGaFL9fDn3OLTvzCGulzE+SzjEe5NGlh5FdCcyfPwps=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fxamacker/cbor/v2 v2.5.0 h1:oHsG0V/Q6E/wqTS2O1Cozzsy69nqCiguo5Q1a1ADivE=
github.com/fxamacker/cbor/v2 v2.5.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
@ -43,28 +49,25 @@ github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjW
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/safing/portbase v0.18.5 h1:BgIBpreSNOnyHqx6Ovx3xJMkX2yOa3A2uLpfVBbfJPM=
github.com/safing/portbase v0.18.5/go.mod h1:qhhLjrr5iEGU9r7RZ6hJdtulOeycJ0d0jq95ZxGJ9Hs=
github.com/safing/structures v1.1.0 h1:QzHBQBjaZSLzw2f6PM4ibSmPcfBHAOB5CKJ+k4FYkhQ=
github.com/safing/structures v1.1.0/go.mod h1:QUrB74FcU41ahQ5oy3YNFCoSq+twE/n3+vNZc2K35II=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tevino/abool v1.2.0 h1:heAkClL8H6w+mK5md9dzsuohKeXHUpY7Vw0ZCKW+huA=
github.com/tevino/abool v1.2.0/go.mod h1:qc66Pna1RiIsPa7O4Egxxs9OqkuxDX55zznh9K07Tzg=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM=
github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U=
github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
@ -72,22 +75,28 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
github.com/vmihailenco/msgpack/v5 v5.4.0 h1:hRM0digJwyR6vll33NNAwCFguy5JuBD6jxDmQP3l608=
github.com/vmihailenco/msgpack/v5 v5.4.0/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zalando/go-keyring v0.2.3 h1:v9CUu9phlABObO4LPWycf+zwMG7nlbb3t/B5wa97yms=
github.com/zalando/go-keyring v0.2.3/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk=
github.com/zalando/go-keyring v0.2.5 h1:Bc2HHpjALryKD62ppdEzaFG6VxL6Bc+5v0LYpN8Lba8=
github.com/zalando/go-keyring v0.2.5/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk=
github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY=
github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg=
github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ=
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY=
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
@ -101,25 +110,27 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -18,7 +18,8 @@ func init() {
Register(blake2bBase.With(&HashTool{
Name: "BLAKE2s-256",
Hash: crypto.BLAKE2s_256,
NewHash: crypto.BLAKE2s_256.New,
CryptoHashID: crypto.BLAKE2b_256,
DigestSize: crypto.BLAKE2s_256.Size(),
BlockSize: crypto.BLAKE2s_256.New().BlockSize(),
SecurityLevel: 128,
@ -27,7 +28,8 @@ func init() {
}))
Register(blake2bBase.With(&HashTool{
Name: "BLAKE2b-256",
Hash: crypto.BLAKE2b_256,
NewHash: crypto.BLAKE2b_256.New,
CryptoHashID: crypto.BLAKE2b_256,
DigestSize: crypto.BLAKE2b_256.Size(),
BlockSize: crypto.BLAKE2b_256.New().BlockSize(),
SecurityLevel: 128,
@ -35,7 +37,8 @@ func init() {
}))
Register(blake2bBase.With(&HashTool{
Name: "BLAKE2b-384",
Hash: crypto.BLAKE2b_384,
NewHash: crypto.BLAKE2b_384.New,
CryptoHashID: crypto.BLAKE2b_384,
DigestSize: crypto.BLAKE2b_384.Size(),
BlockSize: crypto.BLAKE2b_384.New().BlockSize(),
SecurityLevel: 192,
@ -43,7 +46,8 @@ func init() {
}))
Register(blake2bBase.With(&HashTool{
Name: "BLAKE2b-512",
Hash: crypto.BLAKE2b_512,
NewHash: crypto.BLAKE2b_512.New,
CryptoHashID: crypto.BLAKE2b_512,
DigestSize: crypto.BLAKE2b_512.Size(),
BlockSize: crypto.BLAKE2b_512.New().BlockSize(),
SecurityLevel: 256,

26
hashtools/blake3.go Normal file
View file

@ -0,0 +1,26 @@
package hashtools
import (
"hash"
"github.com/zeebo/blake3"
"github.com/safing/jess/lhash"
)
func init() {
Register(&HashTool{
Name: "BLAKE3",
NewHash: newBlake3,
DigestSize: newBlake3().Size(),
BlockSize: newBlake3().BlockSize(),
SecurityLevel: 128,
Comment: "cryptographic hash function based on Bao and BLAKE2",
Author: "Jean-Philippe Aumasson et al., 2020",
labeledAlg: lhash.BLAKE3,
})
}
func newBlake3() hash.Hash {
return blake3.New()
}

View file

@ -10,7 +10,9 @@ import (
// HashTool holds generic information about a hash tool.
type HashTool struct {
Name string
Hash crypto.Hash
NewHash func() hash.Hash
CryptoHashID crypto.Hash
DigestSize int // in bytes
BlockSize int // in bytes
@ -24,7 +26,7 @@ type HashTool struct {
// New returns a new hash.Hash instance of the hash tool.
func (ht *HashTool) New() hash.Hash {
return ht.Hash.New()
return ht.NewHash()
}
// With uses the original HashTool as a template for a new HashTool and returns the new HashTool.
@ -32,8 +34,11 @@ func (ht *HashTool) With(changes *HashTool) *HashTool {
if changes.Name == "" {
changes.Name = ht.Name
}
if changes.Hash == 0 {
changes.Hash = ht.Hash
if changes.NewHash == nil {
changes.NewHash = ht.NewHash
}
if changes.CryptoHashID == 0 {
changes.CryptoHashID = ht.CryptoHashID
}
if changes.DigestSize == 0 {
changes.DigestSize = ht.DigestSize

View file

@ -20,7 +20,8 @@ func init() {
}
Register(sha2Base.With(&HashTool{
Name: "SHA2-224",
Hash: crypto.SHA224,
NewHash: crypto.SHA224.New,
CryptoHashID: crypto.SHA224,
DigestSize: crypto.SHA224.Size(),
BlockSize: crypto.SHA224.New().BlockSize(),
SecurityLevel: 112,
@ -29,7 +30,8 @@ func init() {
}))
Register(sha2Base.With(&HashTool{
Name: "SHA2-256",
Hash: crypto.SHA256,
NewHash: crypto.SHA256.New,
CryptoHashID: crypto.SHA256,
DigestSize: crypto.SHA256.Size(),
BlockSize: crypto.SHA256.New().BlockSize(),
SecurityLevel: 128,
@ -37,7 +39,8 @@ func init() {
}))
Register(sha2Base.With(&HashTool{
Name: "SHA2-384",
Hash: crypto.SHA384,
NewHash: crypto.SHA384.New,
CryptoHashID: crypto.SHA384,
DigestSize: crypto.SHA384.Size(),
BlockSize: crypto.SHA384.New().BlockSize(),
SecurityLevel: 192,
@ -45,7 +48,8 @@ func init() {
}))
Register(sha2Base.With(&HashTool{
Name: "SHA2-512",
Hash: crypto.SHA512,
NewHash: crypto.SHA512.New,
CryptoHashID: crypto.SHA512,
DigestSize: crypto.SHA512.Size(),
BlockSize: crypto.SHA512.New().BlockSize(),
SecurityLevel: 256,
@ -53,7 +57,8 @@ func init() {
}))
Register(sha2Base.With(&HashTool{
Name: "SHA2-512-224",
Hash: crypto.SHA512_224,
NewHash: crypto.SHA512_224.New,
CryptoHashID: crypto.SHA512_224,
DigestSize: crypto.SHA512_224.Size(),
BlockSize: crypto.SHA512_224.New().BlockSize(),
SecurityLevel: 112,
@ -61,7 +66,8 @@ func init() {
}))
Register(sha2Base.With(&HashTool{
Name: "SHA2-512-256",
Hash: crypto.SHA512_256,
NewHash: crypto.SHA512_256.New,
CryptoHashID: crypto.SHA512_256,
DigestSize: crypto.SHA512_256.Size(),
BlockSize: crypto.SHA512_256.New().BlockSize(),
SecurityLevel: 128,
@ -75,7 +81,8 @@ func init() {
}
Register(sha3Base.With(&HashTool{
Name: "SHA3-224",
Hash: crypto.SHA3_224,
NewHash: crypto.SHA3_224.New,
CryptoHashID: crypto.SHA3_224,
DigestSize: crypto.SHA3_224.Size(),
BlockSize: crypto.SHA3_224.New().BlockSize(),
SecurityLevel: 112,
@ -83,7 +90,8 @@ func init() {
}))
Register(sha3Base.With(&HashTool{
Name: "SHA3-256",
Hash: crypto.SHA3_256,
NewHash: crypto.SHA3_256.New,
CryptoHashID: crypto.SHA3_256,
DigestSize: crypto.SHA3_256.Size(),
BlockSize: crypto.SHA3_256.New().BlockSize(),
SecurityLevel: 128,
@ -91,7 +99,8 @@ func init() {
}))
Register(sha3Base.With(&HashTool{
Name: "SHA3-384",
Hash: crypto.SHA3_384,
NewHash: crypto.SHA3_384.New,
CryptoHashID: crypto.SHA3_384,
DigestSize: crypto.SHA3_384.Size(),
BlockSize: crypto.SHA3_384.New().BlockSize(),
SecurityLevel: 192,
@ -99,7 +108,8 @@ func init() {
}))
Register(sha3Base.With(&HashTool{
Name: "SHA3-512",
Hash: crypto.SHA3_512,
NewHash: crypto.SHA3_512.New,
CryptoHashID: crypto.SHA3_512,
DigestSize: crypto.SHA3_512.Size(),
BlockSize: crypto.SHA3_512.New().BlockSize(),
SecurityLevel: 256,

View file

@ -1,16 +1,18 @@
package hashtools
import "testing"
import (
"encoding/hex"
"testing"
)
func TestAll(t *testing.T) {
t.Parallel()
testData := []byte("The quick brown fox jumps over the lazy dog. ")
testData := []byte("The quick brown fox jumps over the lazy dog.")
all := AsList()
for _, hashTool := range all {
// take detour in getting hash.Hash for testing
// Test hash usage.
hash, err := New(hashTool.Name)
if err != nil {
t.Fatalf("failed to get HashTool %s", hashTool.Name)
@ -30,5 +32,97 @@ func TestAll(t *testing.T) {
t.Errorf("hashTool %s is broken or reports invalid digest size. Expected %d, got %d.", hashTool.Name, hashTool.DigestSize, len(sum))
}
// Check hash outputs.
expectedOutputs, ok := testOutputs[hashTool.Name]
if !ok {
t.Errorf("no test outputs available for %s", hashTool.Name)
continue
}
// Test empty string.
hash.Reset()
_, _ = hash.Write(testInputEmpty)
hexSum := hex.EncodeToString(hash.Sum(nil))
if hexSum != expectedOutputs[0] {
t.Errorf("hash tool %s: test empty: digest mismatch, expected %+v, got %+v",
hashTool.Name, expectedOutputs[0], hexSum)
}
// Test fox string.
hash.Reset()
_, _ = hash.Write(testInputFox)
hexSum = hex.EncodeToString(hash.Sum(nil))
if hexSum != expectedOutputs[1] {
t.Errorf("hash tool %s: test empty: digest mismatch, expected %+v, got %+v",
hashTool.Name, expectedOutputs[1], hexSum)
}
}
}
var (
testInputEmpty = []byte("")
testInputFox = []byte("The quick brown fox jumps over the lazy dog.")
)
var testOutputs = map[string][2]string{
"SHA2-224": {
"d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f",
"619cba8e8e05826e9b8c519c0a5c68f4fb653e8a3d8aa04bb2c8cd4c",
},
"SHA2-256": {
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"ef537f25c895bfa782526529a9b63d97aa631564d5d789c2b765448c8635fb6c",
},
"SHA2-384": {
"38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b",
"ed892481d8272ca6df370bf706e4d7bc1b5739fa2177aae6c50e946678718fc67a7af2819a021c2fc34e91bdb63409d7",
},
"SHA2-512": {
"cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e",
"91ea1245f20d46ae9a037a989f54f1f790f0a47607eeb8a14d12890cea77a1bbc6c7ed9cf205e67b7f2b8fd4c7dfd3a7a8617e45f3c463d481c7e586c39ac1ed",
},
"SHA2-512-224": {
"6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4",
"6d6a9279495ec4061769752e7ff9c68b6b0b3c5a281b7917ce0572de",
},
"SHA2-512-256": {
"c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a",
"1546741840f8a492b959d9b8b2344b9b0eb51b004bba35c0aebaac86d45264c3",
},
"SHA3-224": {
"6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7",
"2d0708903833afabdd232a20201176e8b58c5be8a6fe74265ac54db0",
},
"SHA3-256": {
"a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a",
"a80f839cd4f83f6c3dafc87feae470045e4eb0d366397d5c6ce34ba1739f734d",
},
"SHA3-384": {
"0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004",
"1a34d81695b622df178bc74df7124fe12fac0f64ba5250b78b99c1273d4b080168e10652894ecad5f1f4d5b965437fb9",
},
"SHA3-512": {
"a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26",
"18f4f4bd419603f95538837003d9d254c26c23765565162247483f65c50303597bc9ce4d289f21d1c2f1f458828e33dc442100331b35e7eb031b5d38ba6460f8",
},
"BLAKE2s-256": {
"69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9",
"95bca6e1b761dca1323505cc629949a0e03edf11633cc7935bd8b56f393afcf2",
},
"BLAKE2b-256": {
"0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8",
"69d7d3b0afba81826d27024c17f7f183659ed0812cf27b382eaef9fdc29b5712",
},
"BLAKE2b-384": {
"b32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100",
"16d65de1a3caf1c26247234c39af636284c7e19ca448c0de788272081410778852c94d9cef6b939968d4f872c7f78337",
},
"BLAKE2b-512": {
"786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce",
"87af9dc4afe5651b7aa89124b905fd214bf17c79af58610db86a0fb1e0194622a4e9d8e395b352223a8183b0d421c0994b98286cbf8c68a495902e0fe6e2bda2",
},
"BLAKE3": {
"af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262",
"4c9bd68d7f0baa2e167cef98295eb1ec99a3ec8f0656b33dbae943b387f31d5d",
},
}

View file

@ -3,8 +3,8 @@ package jess
import (
"errors"
"github.com/safing/portbase/container"
"github.com/safing/portbase/formats/dsd"
"github.com/safing/structures/container"
"github.com/safing/structures/dsd"
)
/*

View file

@ -3,7 +3,7 @@ package jess
import (
"errors"
"github.com/safing/portbase/container"
"github.com/safing/structures/container"
)
/*

View file

@ -10,8 +10,8 @@ import (
"encoding/json"
"fmt"
"github.com/safing/portbase/container"
"github.com/safing/portbase/formats/dsd"
"github.com/safing/structures/container"
"github.com/safing/structures/dsd"
)
// Letter is the data format for encrypted data at rest or in transit.

View file

@ -18,6 +18,8 @@ import (
// Register BLAKE2 in Go's internal registry.
_ "golang.org/x/crypto/blake2b"
_ "golang.org/x/crypto/blake2s"
"github.com/zeebo/blake3"
)
// Algorithm is an identifier for a hash function.
@ -41,6 +43,8 @@ const (
BLAKE2b_256 Algorithm = 25
BLAKE2b_384 Algorithm = 26
BLAKE2b_512 Algorithm = 27
BLAKE3 Algorithm = 32
)
func (a Algorithm) new() hash.Hash {
@ -70,7 +74,7 @@ func (a Algorithm) new() hash.Hash {
case SHA3_512:
return crypto.SHA3_512.New()
// BLAKE2
// BLAKE2
case BLAKE2s_256:
return crypto.BLAKE2s_256.New()
case BLAKE2b_256:
@ -80,6 +84,10 @@ func (a Algorithm) new() hash.Hash {
case BLAKE2b_512:
return crypto.BLAKE2b_512.New()
// BLAKE3
case BLAKE3:
return blake3.New()
default:
return nil
}
@ -122,6 +130,10 @@ func (a Algorithm) String() string {
case BLAKE2b_512:
return "BLAKE2b_512"
// BLAKE3
case BLAKE3:
return "BLAKE3"
default:
return "unknown"
}

View file

@ -12,7 +12,7 @@ import (
"github.com/mr-tron/base58"
"github.com/safing/portbase/container"
"github.com/safing/structures/container"
)
// LabeledHash represents a typed hash value.

View file

@ -32,27 +32,29 @@ func testAlgorithm(t *testing.T, alg Algorithm, emptyHex, foxHex string) {
// test empty
lh := Digest(alg, testEmpty)
if !bytes.Equal(lh.Bytes()[2:], emptyBytes) {
t.Errorf("alg %d: test empty: digest mismatch, expected %+v, got %+v", alg, emptyBytes, lh.Bytes()[2:])
t.Errorf("alg %s: test empty: digest mismatch, expected %+v, got %+v",
alg, hex.EncodeToString(emptyBytes), hex.EncodeToString(lh.Bytes()[2:]))
}
// test fox
lh = Digest(alg, testFoxData)
if !bytes.Equal(lh.Bytes()[2:], foxBytes) {
t.Errorf("alg %d: test fox: digest mismatch, expected %+v, got %+v", alg, foxBytes, lh.Bytes()[2:])
t.Errorf("alg %s: test fox: digest mismatch, expected %+v, got %+v",
alg, hex.EncodeToString(foxBytes), hex.EncodeToString(lh.Bytes()[2:]))
}
// test matching with serialized/loaded labeled hash
if !lh.Matches(testFoxData) {
t.Errorf("alg %d: failed to match reference", alg)
t.Errorf("alg %s: failed to match reference", alg)
}
if !lh.MatchesString(testFox) {
t.Errorf("alg %d: failed to match reference", alg)
t.Errorf("alg %s: failed to match reference", alg)
}
if lh.Matches(noMatchData) {
t.Errorf("alg %d: failed to non-match garbage", alg)
t.Errorf("alg %s: failed to non-match garbage", alg)
}
if lh.MatchesString(noMatch) {
t.Errorf("alg %d: failed to non-match garbage", alg)
t.Errorf("alg %s: failed to non-match garbage", alg)
}
// Test representations
@ -61,7 +63,7 @@ func testAlgorithm(t *testing.T, alg Algorithm, emptyHex, foxHex string) {
lhs := Digest(alg, testFoxData)
loaded, err := FromHex(lhs.Hex())
if err != nil {
t.Errorf("alg %d: failed to load from hex string: %s", alg, err)
t.Errorf("alg %s: failed to load from hex string: %s", alg, err)
return
}
testFormat(t, alg, lhs, loaded)
@ -70,7 +72,7 @@ func testAlgorithm(t *testing.T, alg Algorithm, emptyHex, foxHex string) {
lhs = Digest(alg, testFoxData)
loaded, err = FromBase64(lhs.Base64())
if err != nil {
t.Errorf("alg %d: failed to load from base64 string: %s", alg, err)
t.Errorf("alg %s: failed to load from base64 string: %s", alg, err)
return
}
testFormat(t, alg, lhs, loaded)
@ -79,7 +81,7 @@ func testAlgorithm(t *testing.T, alg Algorithm, emptyHex, foxHex string) {
lhs = Digest(alg, testFoxData)
loaded, err = FromBase58(lhs.Base58())
if err != nil {
t.Errorf("alg %d: failed to load from base58 string: %s", alg, err)
t.Errorf("alg %s: failed to load from base58 string: %s", alg, err)
return
}
testFormat(t, alg, lhs, loaded)
@ -92,47 +94,88 @@ func testFormat(t *testing.T, alg Algorithm, lhs, loaded *LabeledHash) {
// Test equality.
if !lhs.Equal(loaded) {
t.Errorf("alg %d: equality test failed", alg)
t.Errorf("alg %s: equality test failed", alg)
}
if lhs.Equal(noMatchLH) {
t.Errorf("alg %d: non-equality test failed", alg)
t.Errorf("alg %s: non-equality test failed", alg)
}
// Test matching.
if !loaded.Matches(testFoxData) {
t.Errorf("alg %d: failed to match reference", alg)
t.Errorf("alg %s: failed to match reference", alg)
}
if !loaded.MatchesString(testFox) {
t.Errorf("alg %d: failed to match reference", alg)
t.Errorf("alg %s: failed to match reference", alg)
}
if loaded.Matches(noMatchData) {
t.Errorf("alg %d: failed to non-match garbage", alg)
t.Errorf("alg %s: failed to non-match garbage", alg)
}
if loaded.MatchesString(noMatch) {
t.Errorf("alg %d: failed to non-match garbage", alg)
t.Errorf("alg %s: failed to non-match garbage", alg)
}
}
func TestHash(t *testing.T) {
t.Parallel()
testAlgorithm(t, SHA2_224,
"d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f",
"619cba8e8e05826e9b8c519c0a5c68f4fb653e8a3d8aa04bb2c8cd4c",
)
testAlgorithm(t, SHA2_256,
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"ef537f25c895bfa782526529a9b63d97aa631564d5d789c2b765448c8635fb6c",
)
testAlgorithm(t, SHA2_384,
"38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b",
"ed892481d8272ca6df370bf706e4d7bc1b5739fa2177aae6c50e946678718fc67a7af2819a021c2fc34e91bdb63409d7",
)
testAlgorithm(t, SHA2_512,
"cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e",
"91ea1245f20d46ae9a037a989f54f1f790f0a47607eeb8a14d12890cea77a1bbc6c7ed9cf205e67b7f2b8fd4c7dfd3a7a8617e45f3c463d481c7e586c39ac1ed",
)
testAlgorithm(t, SHA2_512_224,
"6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4",
"6d6a9279495ec4061769752e7ff9c68b6b0b3c5a281b7917ce0572de",
)
testAlgorithm(t, SHA2_512_256,
"c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a",
"1546741840f8a492b959d9b8b2344b9b0eb51b004bba35c0aebaac86d45264c3",
)
testAlgorithm(t, SHA3_224,
"6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7",
"2d0708903833afabdd232a20201176e8b58c5be8a6fe74265ac54db0",
)
testAlgorithm(t, SHA3_256,
"a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a",
"a80f839cd4f83f6c3dafc87feae470045e4eb0d366397d5c6ce34ba1739f734d",
)
testAlgorithm(t, SHA3_384,
"0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004",
"1a34d81695b622df178bc74df7124fe12fac0f64ba5250b78b99c1273d4b080168e10652894ecad5f1f4d5b965437fb9",
)
testAlgorithm(t, SHA3_512,
"a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26",
"18f4f4bd419603f95538837003d9d254c26c23765565162247483f65c50303597bc9ce4d289f21d1c2f1f458828e33dc442100331b35e7eb031b5d38ba6460f8",
)
testAlgorithm(t, BLAKE2s_256,
"69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9",
"95bca6e1b761dca1323505cc629949a0e03edf11633cc7935bd8b56f393afcf2",
)
testAlgorithm(t, BLAKE2b_256,
"0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8",
"69d7d3b0afba81826d27024c17f7f183659ed0812cf27b382eaef9fdc29b5712",
)
testAlgorithm(t, BLAKE2b_384,
"b32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100",
"16d65de1a3caf1c26247234c39af636284c7e19ca448c0de788272081410778852c94d9cef6b939968d4f872c7f78337",
)
testAlgorithm(t, BLAKE2b_512,
"786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce",
"87af9dc4afe5651b7aa89124b905fd214bf17c79af58610db86a0fb1e0194622a4e9d8e395b352223a8183b0d421c0994b98286cbf8c68a495902e0fe6e2bda2",
)
testAlgorithm(t, BLAKE3,
"af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262",
"4c9bd68d7f0baa2e167cef98295eb1ec99a3ec8f0656b33dbae943b387f31d5d",
)
}

View file

@ -11,7 +11,7 @@ import (
uuid "github.com/satori/go.uuid"
"github.com/safing/jess/tools"
"github.com/safing/portbase/formats/dsd"
"github.com/safing/structures/dsd"
)
// Special signet types.

View file

@ -1,72 +1,7 @@
package jess
// Currently Recommended Suites.
var (
// Suite Lists.
suitesMap = make(map[string]*Suite)
suitesList []*Suite
// Suite Definitions.
// SuiteKeyV1 is a cipher suite for encryption with a key.
SuiteKeyV1 = registerSuite(&Suite{
ID: "key_v1",
Tools: []string{"HKDF(BLAKE2b-256)", "CHACHA20-POLY1305"},
Provides: NewRequirements(),
SecurityLevel: 128,
Status: SuiteStatusRecommended,
})
// SuitePasswordV1 is a cipher suite for encryption with a password.
SuitePasswordV1 = registerSuite(&Suite{
ID: "pw_v1",
Tools: []string{"SCRYPT-20", "HKDF(BLAKE2b-256)", "CHACHA20-POLY1305"},
Provides: NewRequirements(),
SecurityLevel: 128,
Status: SuiteStatusRecommended,
})
// SuiteRcptOnlyV1 is a cipher suite for encrypting for someone, but without verifying the sender/source.
SuiteRcptOnlyV1 = registerSuite(&Suite{
ID: "rcpt_v1",
Tools: []string{"ECDH-X25519", "HKDF(BLAKE2b-256)", "CHACHA20-POLY1305"},
Provides: NewRequirements().Remove(SenderAuthentication),
SecurityLevel: 128,
Status: SuiteStatusRecommended,
})
// SuiteSignV1 is a cipher suite for signing (no encryption).
SuiteSignV1 = registerSuite(&Suite{
ID: "sign_v1",
Tools: []string{"Ed25519(BLAKE2b-256)"},
Provides: newEmptyRequirements().Add(Integrity).Add(SenderAuthentication),
SecurityLevel: 128,
Status: SuiteStatusRecommended,
})
// SuiteSignFileV1 is a cipher suite for signing files (no encryption).
// SHA2_256 is chosen for better compatibility with other tool sets and workflows.
SuiteSignFileV1 = registerSuite(&Suite{
ID: "signfile_v1",
Tools: []string{"Ed25519(SHA2-256)"},
Provides: newEmptyRequirements().Add(Integrity).Add(SenderAuthentication),
SecurityLevel: 128,
Status: SuiteStatusRecommended,
})
// SuiteCompleteV1 is a cipher suite for both encrypting for someone and signing.
SuiteCompleteV1 = registerSuite(&Suite{
ID: "v1",
Tools: []string{"ECDH-X25519", "Ed25519(BLAKE2b-256)", "HKDF(BLAKE2b-256)", "CHACHA20-POLY1305"},
Provides: NewRequirements(),
SecurityLevel: 128,
Status: SuiteStatusRecommended,
})
// SuiteWireV1 is a cipher suite for network communication, including authentication of the server, but not the client.
SuiteWireV1 = registerSuite(&Suite{
ID: "w1",
Tools: []string{"ECDH-X25519", "HKDF(BLAKE2b-256)", "CHACHA20-POLY1305"},
Provides: NewRequirements().Remove(SenderAuthentication),
SecurityLevel: 128,
Status: SuiteStatusRecommended,
})
// Currently Recommended Suites.
// SuiteKey is a cipher suite for encryption with a key.
SuiteKey = SuiteKeyV1
// SuitePassword is a cipher suite for encryption with a password.
@ -83,6 +18,12 @@ var (
SuiteWire = SuiteWireV1
)
// Suite Lists.
var (
suitesMap = make(map[string]*Suite)
suitesList []*Suite
)
func registerSuite(suite *Suite) (suiteID string) {
// add if not exists
_, ok := suitesMap[suite.ID]

61
suites_v1.go Normal file
View file

@ -0,0 +1,61 @@
package jess //nolint:dupl
var (
// SuiteKeyV1 is a cipher suite for encryption with a key.
SuiteKeyV1 = registerSuite(&Suite{
ID: "key_v1",
Tools: []string{"HKDF(BLAKE2b-256)", "CHACHA20-POLY1305"},
Provides: NewRequirements(),
SecurityLevel: 128,
Status: SuiteStatusRecommended,
})
// SuitePasswordV1 is a cipher suite for encryption with a password.
SuitePasswordV1 = registerSuite(&Suite{
ID: "pw_v1",
Tools: []string{"SCRYPT-20", "HKDF(BLAKE2b-256)", "CHACHA20-POLY1305"},
Provides: NewRequirements(),
SecurityLevel: 128,
Status: SuiteStatusRecommended,
})
// SuiteRcptOnlyV1 is a cipher suite for encrypting for someone, but without verifying the sender/source.
SuiteRcptOnlyV1 = registerSuite(&Suite{
ID: "rcpt_v1",
Tools: []string{"ECDH-X25519", "HKDF(BLAKE2b-256)", "CHACHA20-POLY1305"},
Provides: NewRequirements().Remove(SenderAuthentication),
SecurityLevel: 128,
Status: SuiteStatusRecommended,
})
// SuiteSignV1 is a cipher suite for signing (no encryption).
SuiteSignV1 = registerSuite(&Suite{
ID: "sign_v1",
Tools: []string{"Ed25519(BLAKE2b-256)"},
Provides: newEmptyRequirements().Add(Integrity).Add(SenderAuthentication),
SecurityLevel: 128,
Status: SuiteStatusRecommended,
})
// SuiteSignFileV1 is a cipher suite for signing files (no encryption).
// SHA2_256 is chosen for better compatibility with other tool sets and workflows.
SuiteSignFileV1 = registerSuite(&Suite{
ID: "signfile_v1",
Tools: []string{"Ed25519(SHA2-256)"},
Provides: newEmptyRequirements().Add(Integrity).Add(SenderAuthentication),
SecurityLevel: 128,
Status: SuiteStatusRecommended,
})
// SuiteCompleteV1 is a cipher suite for both encrypting for someone and signing.
SuiteCompleteV1 = registerSuite(&Suite{
ID: "v1",
Tools: []string{"ECDH-X25519", "Ed25519(BLAKE2b-256)", "HKDF(BLAKE2b-256)", "CHACHA20-POLY1305"},
Provides: NewRequirements(),
SecurityLevel: 128,
Status: SuiteStatusRecommended,
})
// SuiteWireV1 is a cipher suite for network communication, including authentication of the server, but not the client.
SuiteWireV1 = registerSuite(&Suite{
ID: "w1",
Tools: []string{"ECDH-X25519", "HKDF(BLAKE2b-256)", "CHACHA20-POLY1305"},
Provides: NewRequirements().Remove(SenderAuthentication),
SecurityLevel: 128,
Status: SuiteStatusRecommended,
})
)

61
suites_v2.go Normal file
View file

@ -0,0 +1,61 @@
package jess //nolint:dupl
var (
// SuiteKeyV2 is a cipher suite for encryption with a key.
SuiteKeyV2 = registerSuite(&Suite{
ID: "key_v2",
Tools: []string{"BLAKE3-KDF", "CHACHA20-POLY1305"},
Provides: NewRequirements(),
SecurityLevel: 128,
Status: SuiteStatusPermitted,
})
// SuitePasswordV2 is a cipher suite for encryption with a password.
SuitePasswordV2 = registerSuite(&Suite{
ID: "pw_v2",
Tools: []string{"SCRYPT-20", "BLAKE3-KDF", "CHACHA20-POLY1305"},
Provides: NewRequirements(),
SecurityLevel: 128,
Status: SuiteStatusPermitted,
})
// SuiteRcptOnlyV2 is a cipher suite for encrypting for someone, but without verifying the sender/source.
SuiteRcptOnlyV2 = registerSuite(&Suite{
ID: "rcpt_v2",
Tools: []string{"ECDH-X25519", "BLAKE3-KDF", "CHACHA20-POLY1305"},
Provides: NewRequirements().Remove(SenderAuthentication),
SecurityLevel: 128,
Status: SuiteStatusPermitted,
})
// SuiteSignV2 is a cipher suite for signing (no encryption).
SuiteSignV2 = registerSuite(&Suite{
ID: "sign_v2",
Tools: []string{"Ed25519(BLAKE3)"},
Provides: newEmptyRequirements().Add(Integrity).Add(SenderAuthentication),
SecurityLevel: 128,
Status: SuiteStatusPermitted,
})
// SuiteSignFileV2 is a cipher suite for signing files (no encryption).
// SHA2_256 is chosen for better compatibility with other tool sets and workflows.
SuiteSignFileV2 = registerSuite(&Suite{
ID: "signfile_v2",
Tools: []string{"Ed25519(BLAKE3)"},
Provides: newEmptyRequirements().Add(Integrity).Add(SenderAuthentication),
SecurityLevel: 128,
Status: SuiteStatusPermitted,
})
// SuiteCompleteV2 is a cipher suite for both encrypting for someone and signing.
SuiteCompleteV2 = registerSuite(&Suite{
ID: "v2",
Tools: []string{"ECDH-X25519", "Ed25519(BLAKE3)", "BLAKE3-KDF", "CHACHA20-POLY1305"},
Provides: NewRequirements(),
SecurityLevel: 128,
Status: SuiteStatusPermitted,
})
// SuiteWireV2 is a cipher suite for network communication, including authentication of the server, but not the client.
SuiteWireV2 = registerSuite(&Suite{
ID: "w2",
Tools: []string{"ECDH-X25519", "BLAKE3-KDF", "CHACHA20-POLY1305"},
Provides: NewRequirements().Remove(SenderAuthentication),
SecurityLevel: 128,
Status: SuiteStatusPermitted,
})
)

View file

@ -3,6 +3,7 @@ package all
import (
// Import all tool subpackages.
_ "github.com/safing/jess/tools/blake3"
_ "github.com/safing/jess/tools/ecdh"
_ "github.com/safing/jess/tools/gostdlib"
)

68
tools/blake3/kdf.go Normal file
View file

@ -0,0 +1,68 @@
package blake3
import (
"errors"
"fmt"
"io"
"github.com/zeebo/blake3"
"github.com/safing/jess/tools"
)
func init() {
tools.Register(&tools.Tool{
Info: &tools.ToolInfo{
Name: "BLAKE3-KDF",
Purpose: tools.PurposeKeyDerivation,
SecurityLevel: 128,
Comment: "cryptographic hash function based on Bao and BLAKE2",
Author: "Jean-Philippe Aumasson et al., 2020",
},
Factory: func() tools.ToolLogic { return &KDF{} },
})
}
// KDF implements the cryptographic interface for BLAKE3 key derivation.
type KDF struct {
tools.ToolLogicBase
reader io.Reader
}
// InitKeyDerivation implements the ToolLogic interface.
func (keyder *KDF) InitKeyDerivation(nonce []byte, material ...[]byte) error {
// Check params.
if len(material) < 1 || len(material[0]) == 0 || len(nonce) == 0 {
return errors.New("must supply at least one key and a nonce as key material")
}
// Setup KDF.
// Use nonce as kdf context.
h := blake3.NewDeriveKey(string(nonce))
// Then add all the key material.
for _, m := range material {
_, _ = h.Write(m)
}
// Get key reader.
keyder.reader = h.Digest()
return nil
}
// DeriveKey implements the ToolLogic interface.
func (keyder *KDF) DeriveKey(size int) ([]byte, error) {
key := make([]byte, size)
return key, keyder.DeriveKeyWriteTo(key)
}
// DeriveKeyWriteTo implements the ToolLogic interface.
func (keyder *KDF) DeriveKeyWriteTo(newKey []byte) error {
n, err := io.ReadFull(keyder.reader, newKey)
if err != nil {
return fmt.Errorf("failed to generate key: %w", err)
}
if n != len(newKey) {
return errors.New("failed to generate key: EOF")
}
return nil
}

View file

@ -9,7 +9,7 @@ import (
"github.com/aead/ecdh"
"github.com/safing/jess/tools"
"github.com/safing/portbase/container"
"github.com/safing/structures/container"
)
var nistCurveInfo = &tools.ToolInfo{

View file

@ -7,7 +7,7 @@ import (
"github.com/aead/ecdh"
"github.com/safing/jess/tools"
"github.com/safing/portbase/container"
"github.com/safing/structures/container"
)
func init() {

View file

@ -6,7 +6,7 @@ import (
"errors"
"github.com/safing/jess/tools"
"github.com/safing/portbase/container"
"github.com/safing/structures/container"
)
func init() {

View file

@ -8,7 +8,7 @@ import (
"golang.org/x/crypto/hkdf"
"github.com/safing/jess/tools"
"github.com/safing/portbase/container"
"github.com/safing/structures/container"
)
func init() {

View file

@ -8,7 +8,7 @@ import (
"math/big"
"github.com/safing/jess/tools"
"github.com/safing/portbase/container"
"github.com/safing/structures/container"
)
type rsaBase struct {

View file

@ -2,6 +2,7 @@ package gostdlib
import (
"crypto/rsa"
"errors"
"github.com/safing/jess/tools"
)
@ -38,11 +39,14 @@ func (pss *RsaPSS) Sign(data, associatedData []byte, signet tools.SignetInt) ([]
if err != nil {
return nil, err
}
if pss.HashTool().CryptoHashID == 0 {
return nil, errors.New("tool PSS is only compatible with Golang crypto.Hash hash functions")
}
return rsa.SignPSS(
pss.Helper().Random(),
rsaPrivKey,
pss.HashTool().Hash,
pss.HashTool().CryptoHashID,
hashsum,
nil, // *rsa.PSSOptions
)
@ -59,10 +63,13 @@ func (pss *RsaPSS) Verify(data, associatedData, signature []byte, signet tools.S
if err != nil {
return err
}
if pss.HashTool().CryptoHashID == 0 {
return errors.New("tool PSS is only compatible with Golang crypto.Hash hash functions")
}
return rsa.VerifyPSS(
rsaPubKey,
pss.HashTool().Hash,
pss.HashTool().CryptoHashID,
hashsum,
signature,
nil, // *rsa.PSSOptions

View file

@ -6,7 +6,7 @@ import (
"os"
"github.com/safing/jess"
"github.com/safing/portbase/formats/dsd"
"github.com/safing/structures/dsd"
)
// WriteSignetToFile serializes the signet and writes it to the given file.