Add support for json file signing
This commit is contained in:
parent
4fbce7d649
commit
4c4b4471d8
4 changed files with 215 additions and 25 deletions
|
@ -1,6 +1,7 @@
|
||||||
package filesig
|
package filesig
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
@ -9,7 +10,9 @@ import (
|
||||||
"github.com/tidwall/sjson"
|
"github.com/tidwall/sjson"
|
||||||
"golang.org/x/exp/slices"
|
"golang.org/x/exp/slices"
|
||||||
|
|
||||||
|
"github.com/safing/jess"
|
||||||
"github.com/safing/jess/lhash"
|
"github.com/safing/jess/lhash"
|
||||||
|
"github.com/safing/structures/dsd"
|
||||||
)
|
)
|
||||||
|
|
||||||
// JSON file metadata keys.
|
// JSON file metadata keys.
|
||||||
|
@ -32,10 +35,10 @@ func AddJSONChecksum(data []byte) ([]byte, error) {
|
||||||
checksums = append(checksums, h.Base58())
|
checksums = append(checksums, h.Base58())
|
||||||
|
|
||||||
// Sort and deduplicate checksums and sigs.
|
// Sort and deduplicate checksums and sigs.
|
||||||
slices.Sort[[]string, string](checksums)
|
slices.Sort(checksums)
|
||||||
checksums = slices.Compact[[]string, string](checksums)
|
checksums = slices.Compact(checksums)
|
||||||
slices.Sort[[]string, string](signatures)
|
slices.Sort(signatures)
|
||||||
signatures = slices.Compact[[]string, string](signatures)
|
signatures = slices.Compact(signatures)
|
||||||
|
|
||||||
// Add metadata and return.
|
// Add metadata and return.
|
||||||
return jsonAddMeta(content, checksums, signatures)
|
return jsonAddMeta(content, checksums, signatures)
|
||||||
|
@ -72,6 +75,86 @@ func VerifyJSONChecksum(data []byte) error {
|
||||||
return nil
|
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) (
|
func jsonSplit(data []byte) (
|
||||||
content []byte,
|
content []byte,
|
||||||
checksums []string,
|
checksums []string,
|
||||||
|
@ -187,10 +270,9 @@ func jsonAddMeta(data []byte, checksums, signatures []string) ([]byte, error) {
|
||||||
|
|
||||||
// Final pretty print.
|
// Final pretty print.
|
||||||
data = pretty.PrettyOptions(data, &pretty.Options{
|
data = pretty.PrettyOptions(data, &pretty.Options{
|
||||||
Width: 200, // Must not change!
|
Width: 200, // Must not change!
|
||||||
Prefix: "", // Must not change!
|
Prefix: "", // Must not change!
|
||||||
Indent: " ", // Must not change!
|
Indent: " ", // Must not change!
|
||||||
SortKeys: true, // Must not change!
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return data, nil
|
return data, nil
|
||||||
|
|
|
@ -4,6 +4,10 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/safing/jess"
|
||||||
|
"github.com/safing/jess/tools"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestJSONChecksums(t *testing.T) {
|
func TestJSONChecksums(t *testing.T) {
|
||||||
|
@ -22,9 +26,9 @@ func TestJSONChecksums(t *testing.T) {
|
||||||
`
|
`
|
||||||
|
|
||||||
testJSONWithChecksum, err := AddJSONChecksum([]byte(json))
|
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.Equal(t, jsonWithChecksum, string(testJSONWithChecksum), "should match")
|
||||||
assert.NoError(t,
|
require.NoError(t,
|
||||||
VerifyJSONChecksum(testJSONWithChecksum),
|
VerifyJSONChecksum(testJSONWithChecksum),
|
||||||
"checksum should be correct",
|
"checksum should be correct",
|
||||||
)
|
)
|
||||||
|
@ -33,7 +37,7 @@ func TestJSONChecksums(t *testing.T) {
|
||||||
"c": 1, "a":"b",
|
"c": 1, "a":"b",
|
||||||
"_jess-checksum": "ZwtAd75qvioh6uf1NAq64KRgTbqeehFVYmhLmrwu1s7xJo"
|
"_jess-checksum": "ZwtAd75qvioh6uf1NAq64KRgTbqeehFVYmhLmrwu1s7xJo"
|
||||||
}`
|
}`
|
||||||
assert.NoError(t,
|
require.NoError(t,
|
||||||
VerifyJSONChecksum([]byte(jsonWithChecksum)),
|
VerifyJSONChecksum([]byte(jsonWithChecksum)),
|
||||||
"checksum should be correct",
|
"checksum should be correct",
|
||||||
)
|
)
|
||||||
|
@ -48,7 +52,7 @@ func TestJSONChecksums(t *testing.T) {
|
||||||
"c": 1
|
"c": 1
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
assert.NoError(t,
|
require.NoError(t,
|
||||||
VerifyJSONChecksum([]byte(jsonWithMultiChecksum)),
|
VerifyJSONChecksum([]byte(jsonWithMultiChecksum)),
|
||||||
"checksum should be correct",
|
"checksum should be correct",
|
||||||
)
|
)
|
||||||
|
@ -61,9 +65,9 @@ func TestJSONChecksums(t *testing.T) {
|
||||||
`
|
`
|
||||||
|
|
||||||
testJSONWithMultiChecksum, err := AddJSONChecksum([]byte(jsonWithMultiChecksum))
|
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.Equal(t, jsonWithMultiChecksumOutput, string(testJSONWithMultiChecksum), "should match")
|
||||||
assert.NoError(t,
|
require.NoError(t,
|
||||||
VerifyJSONChecksum(testJSONWithMultiChecksum),
|
VerifyJSONChecksum(testJSONWithMultiChecksum),
|
||||||
"checksum should be correct",
|
"checksum should be correct",
|
||||||
)
|
)
|
||||||
|
@ -117,3 +121,106 @@ func TestJSONChecksums(t *testing.T) {
|
||||||
//
|
//
|
||||||
// assert.Error(t, VerifyTextFileChecksum([]byte(textWithFailingChecksums), "#"), "should fail")
|
// 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",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ func SignFileData(fileHash *lhash.LabeledHash, metaData map[string]string, envel
|
||||||
|
|
||||||
// Check if the envelope is suitable for signing.
|
// Check if the envelope is suitable for signing.
|
||||||
if err := envelope.Suite().Provides.CheckComplianceTo(fileSigRequirements); err != nil {
|
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.
|
// Create struct and transform data into serializable format to be signed.
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTextChecksums(t *testing.T) {
|
func TestTextChecksums(t *testing.T) {
|
||||||
|
@ -29,20 +30,20 @@ do_something()
|
||||||
`
|
`
|
||||||
|
|
||||||
testTextWithChecksumAfterComment, err := AddTextFileChecksum([]byte(text), "#", TextPlacementAfterComment)
|
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.Equal(t, textWithChecksumAfterComment, string(testTextWithChecksumAfterComment), "should match")
|
||||||
assert.NoError(t,
|
require.NoError(t,
|
||||||
VerifyTextFileChecksum(testTextWithChecksumAfterComment, "#"),
|
VerifyTextFileChecksum(testTextWithChecksumAfterComment, "#"),
|
||||||
"checksum should be correct",
|
"checksum should be correct",
|
||||||
)
|
)
|
||||||
assert.NoError(t,
|
require.NoError(t,
|
||||||
VerifyTextFileChecksum(append(
|
VerifyTextFileChecksum(append(
|
||||||
[]byte("\n\n \r\n"),
|
[]byte("\n\n \r\n"),
|
||||||
testTextWithChecksumAfterComment...,
|
testTextWithChecksumAfterComment...,
|
||||||
), "#"),
|
), "#"),
|
||||||
"checksum should be correct",
|
"checksum should be correct",
|
||||||
)
|
)
|
||||||
assert.NoError(t,
|
require.NoError(t,
|
||||||
VerifyTextFileChecksum(append(
|
VerifyTextFileChecksum(append(
|
||||||
testTextWithChecksumAfterComment,
|
testTextWithChecksumAfterComment,
|
||||||
[]byte("\r\n \n \n")...,
|
[]byte("\r\n \n \n")...,
|
||||||
|
@ -62,9 +63,9 @@ do_something()
|
||||||
`
|
`
|
||||||
|
|
||||||
testTextWithChecksumAtTop, err := AddTextFileChecksum([]byte(text), "#", TextPlacementTop)
|
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.Equal(t, textWithChecksumAtTop, string(testTextWithChecksumAtTop), "should match")
|
||||||
assert.NoError(t,
|
require.NoError(t,
|
||||||
VerifyTextFileChecksum(testTextWithChecksumAtTop, "#"),
|
VerifyTextFileChecksum(testTextWithChecksumAtTop, "#"),
|
||||||
"checksum should be correct",
|
"checksum should be correct",
|
||||||
)
|
)
|
||||||
|
@ -82,9 +83,9 @@ do_something()
|
||||||
`
|
`
|
||||||
|
|
||||||
testTextWithChecksumAtBottom, err := AddTextFileChecksum([]byte(text), "#", TextPlacementBottom)
|
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.Equal(t, textWithChecksumAtBottom, string(testTextWithChecksumAtBottom), "should match")
|
||||||
assert.NoError(t,
|
require.NoError(t,
|
||||||
VerifyTextFileChecksum(testTextWithChecksumAtBottom, "#"),
|
VerifyTextFileChecksum(testTextWithChecksumAtBottom, "#"),
|
||||||
"checksum should be correct",
|
"checksum should be correct",
|
||||||
)
|
)
|
||||||
|
@ -119,7 +120,7 @@ do_something()
|
||||||
do_something()
|
do_something()
|
||||||
`
|
`
|
||||||
testTextWithMultiChecksumOutput, err := AddTextFileChecksum([]byte(textWithMultiChecksum), "#", TextPlacementAfterComment)
|
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")
|
assert.Equal(t, textWithMultiChecksumOutput, string(testTextWithMultiChecksumOutput), "should match")
|
||||||
|
|
||||||
// Test failing checksums.
|
// Test failing checksums.
|
||||||
|
@ -135,7 +136,7 @@ do_something()
|
||||||
|
|
||||||
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) {
|
func TestLineEndDetection(t *testing.T) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue