Remediate SAF-01-005 Crypto: Unnecessary Configurability Considered Dangerous (Medium)
This is a rather large commit, as the change from a toolset to the new cipher suite system has a large impact.
This commit is contained in:
parent
7990775cf3
commit
31216b0885
20 changed files with 1117 additions and 389 deletions
4
Gopkg.lock
generated
4
Gopkg.lock
generated
|
@ -104,7 +104,7 @@
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:23e3d5f66ff9fd60dd264fbf26e71cab6b4a3bd2ef993a1645432d5c4c3b50fc"
|
digest = "1:4df631634d7dc4496e9c51436cc2d3ccd0d0d4734640f63108950fe68b348b7e"
|
||||||
name = "golang.org/x/crypto"
|
name = "golang.org/x/crypto"
|
||||||
packages = [
|
packages = [
|
||||||
"blake2b",
|
"blake2b",
|
||||||
|
@ -118,6 +118,7 @@
|
||||||
"poly1305",
|
"poly1305",
|
||||||
"salsa20",
|
"salsa20",
|
||||||
"salsa20/salsa",
|
"salsa20/salsa",
|
||||||
|
"scrypt",
|
||||||
"sha3",
|
"sha3",
|
||||||
]
|
]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
|
@ -164,6 +165,7 @@
|
||||||
"golang.org/x/crypto/pbkdf2",
|
"golang.org/x/crypto/pbkdf2",
|
||||||
"golang.org/x/crypto/poly1305",
|
"golang.org/x/crypto/poly1305",
|
||||||
"golang.org/x/crypto/salsa20",
|
"golang.org/x/crypto/salsa20",
|
||||||
|
"golang.org/x/crypto/scrypt",
|
||||||
"golang.org/x/crypto/sha3",
|
"golang.org/x/crypto/sha3",
|
||||||
]
|
]
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
|
|
2
SPEC.md
2
SPEC.md
|
@ -1,5 +1,7 @@
|
||||||
# Jess Specification
|
# Jess Specification
|
||||||
|
|
||||||
|
This document takes a closer look at the inner workings of Jess. It does not, however, define the formats and some other aspects. First, some basics will be covered. Then, the wire protocol is given a closer look.
|
||||||
|
|
||||||
## Basics
|
## Basics
|
||||||
|
|
||||||
The basic building blocks of jess are:
|
The basic building blocks of jess are:
|
||||||
|
|
|
@ -11,20 +11,19 @@ import (
|
||||||
func TestWire(t *testing.T) {
|
func TestWire(t *testing.T) {
|
||||||
wireReKeyAfterMsgs = 100
|
wireReKeyAfterMsgs = 100
|
||||||
|
|
||||||
testWireCorrespondence(t, RecommendedNetwork, testData1)
|
// current suites recommendation
|
||||||
testWireCorrespondence(t, RecommendedNetwork, testData2)
|
testWireCorrespondence(t, getSuite(t, SuiteWire), testData1)
|
||||||
|
testWireCorrespondence(t, getSuite(t, SuiteWire), testData2)
|
||||||
|
|
||||||
testWireCorrespondence(t, []string{"ECDH-P224", "HKDF(SHA2-256)", "CHACHA20-POLY1305"}, testData1)
|
// older suites
|
||||||
testWireCorrespondence(t, []string{"ECDH-P256", "HKDF(SHA2-256)", "CHACHA20-POLY1305"}, testData1)
|
// testWireCorrespondence(t, getSuite(t, SuiteWireV1), testData1)
|
||||||
testWireCorrespondence(t, []string{"ECDH-P384", "HKDF(SHA2-256)", "CHACHA20-POLY1305"}, testData1)
|
// testWireCorrespondence(t, getSuite(t, SuiteWireV1), testData2)
|
||||||
testWireCorrespondence(t, []string{"ECDH-P521", "HKDF(SHA2-256)", "CHACHA20-POLY1305"}, testData1)
|
|
||||||
testWireCorrespondence(t, []string{"RSA-OAEP(SHA2-256)", "HKDF(SHA2-256)", "CHACHA20-POLY1305"}, testData1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func testWireCorrespondence(t *testing.T, toolIDs []string, testData string) {
|
func testWireCorrespondence(t *testing.T, suite *Suite, testData string) {
|
||||||
wtr := &wireTestRange{t: t}
|
wtr := &wireTestRange{t: t}
|
||||||
wtr.init(toolIDs, testData)
|
wtr.init(suite, testData)
|
||||||
fmt.Printf("\n\nsimulating %v\n", toolIDs)
|
fmt.Printf("\n\nsimulating %v\n", suite.ID)
|
||||||
fmt.Println("two dots are one packet send+recv:")
|
fmt.Println("two dots are one packet send+recv:")
|
||||||
|
|
||||||
fmt.Println("\nclient ->")
|
fmt.Println("\nclient ->")
|
||||||
|
@ -74,7 +73,7 @@ func testWireCorrespondence(t *testing.T, toolIDs []string, testData string) {
|
||||||
duration := wtr.endTime.Sub(wtr.startTime)
|
duration := wtr.endTime.Sub(wtr.startTime)
|
||||||
t.Logf(
|
t.Logf(
|
||||||
"%v tested: msgsize=%d, rekey every %d msgs, %d msgs, %d bytes, +%f%% overhead, %s, %s per msg, %f Mbit/s",
|
"%v tested: msgsize=%d, rekey every %d msgs, %d msgs, %d bytes, +%f%% overhead, %s, %s per msg, %f Mbit/s",
|
||||||
wtr.toolIDs,
|
wtr.suite.ID,
|
||||||
len(testData),
|
len(testData),
|
||||||
wireReKeyAfterMsgs,
|
wireReKeyAfterMsgs,
|
||||||
wtr.msgsTransferred,
|
wtr.msgsTransferred,
|
||||||
|
@ -92,7 +91,7 @@ func testWireCorrespondence(t *testing.T, toolIDs []string, testData string) {
|
||||||
|
|
||||||
type wireTestRange struct {
|
type wireTestRange struct {
|
||||||
t *testing.T
|
t *testing.T
|
||||||
toolIDs []string
|
suite *Suite
|
||||||
testData string
|
testData string
|
||||||
|
|
||||||
client *Session
|
client *Session
|
||||||
|
@ -108,12 +107,12 @@ type wireTestRange struct {
|
||||||
endTime time.Time
|
endTime time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wtr *wireTestRange) init(toolIDs []string, testData string) (detectedInvalid bool) {
|
func (wtr *wireTestRange) init(suite *Suite, testData string) (detectedInvalid bool) {
|
||||||
wtr.toolIDs = toolIDs
|
wtr.suite = suite
|
||||||
|
|
||||||
e, err := setupEnvelopeAndTrustStore(wtr.t, wtr.toolIDs)
|
e, err := setupEnvelopeAndTrustStore(wtr.t, wtr.suite)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wtr.t.Fatalf("%v failed to setup envelope: %s", wtr.toolIDs, err)
|
wtr.t.Fatalf("%s failed to setup envelope: %s", wtr.suite.ID, err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if e == nil {
|
if e == nil {
|
||||||
|
@ -122,7 +121,7 @@ func (wtr *wireTestRange) init(toolIDs []string, testData string) (detectedInval
|
||||||
|
|
||||||
wtr.client, err = e.WireCorrespondence(testTrustStore)
|
wtr.client, err = e.WireCorrespondence(testTrustStore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wtr.t.Fatalf("%v failed to init client session: %s", wtr.toolIDs, err)
|
wtr.t.Fatalf("%s failed to init client session: %s", wtr.suite.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup and reset
|
// setup and reset
|
||||||
|
@ -142,18 +141,18 @@ func (wtr *wireTestRange) init(toolIDs []string, testData string) (detectedInval
|
||||||
func (wtr *wireTestRange) clientSend() {
|
func (wtr *wireTestRange) clientSend() {
|
||||||
letter, err := wtr.client.Close([]byte(wtr.testData))
|
letter, err := wtr.client.Close([]byte(wtr.testData))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wtr.t.Fatalf("%v failed to close: %s", wtr.toolIDs, err)
|
wtr.t.Fatalf("%s failed to close: %s", wtr.suite.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
wireData, err := letter.ToWire()
|
wireData, err := letter.ToWire()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wtr.t.Fatalf("%v failed to serialize to wire: %s", wtr.toolIDs, err)
|
wtr.t.Fatalf("%s failed to serialize to wire: %s", wtr.suite.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case wtr.clientToServer <- wireData:
|
case wtr.clientToServer <- wireData:
|
||||||
default:
|
default:
|
||||||
wtr.t.Fatalf("%v could not send to server", wtr.toolIDs)
|
wtr.t.Fatalf("%s could not send to server", wtr.suite.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Print(".")
|
fmt.Print(".")
|
||||||
|
@ -167,27 +166,27 @@ func (wtr *wireTestRange) serverRecv() {
|
||||||
|
|
||||||
letter, err := LetterFromWire(wireData)
|
letter, err := LetterFromWire(wireData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wtr.t.Fatalf("%v failed to parse initial wired letter: %s", wtr.toolIDs, err)
|
wtr.t.Fatalf("%s failed to parse initial wired letter: %s", wtr.suite.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if wtr.server == nil {
|
if wtr.server == nil {
|
||||||
wtr.server, err = letter.WireCorrespondence(testTrustStore)
|
wtr.server, err = letter.WireCorrespondence(testTrustStore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wtr.t.Fatalf("%v failed to init server session: %s", wtr.toolIDs, err)
|
wtr.t.Fatalf("%s failed to init server session: %s", wtr.suite.ID, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
origData, err := wtr.server.Open(letter)
|
origData, err := wtr.server.Open(letter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wtr.t.Fatalf("%v failed to open: %s", wtr.toolIDs, err)
|
wtr.t.Fatalf("%s failed to open: %s", wtr.suite.ID, err)
|
||||||
}
|
}
|
||||||
wtr.bytesTransferred += len(origData)
|
wtr.bytesTransferred += len(origData)
|
||||||
|
|
||||||
if string(origData) != wtr.testData {
|
if string(origData) != wtr.testData {
|
||||||
wtr.t.Fatalf("%v testdata mismatch", wtr.toolIDs)
|
wtr.t.Fatalf("%s testdata mismatch", wtr.suite.ID)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
wtr.t.Fatalf("%v could not recv from client", wtr.toolIDs)
|
wtr.t.Fatalf("%s could not recv from client", wtr.suite.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Print(".")
|
fmt.Print(".")
|
||||||
|
@ -196,18 +195,18 @@ func (wtr *wireTestRange) serverRecv() {
|
||||||
func (wtr *wireTestRange) serverSend() {
|
func (wtr *wireTestRange) serverSend() {
|
||||||
letter, err := wtr.server.Close([]byte(wtr.testData))
|
letter, err := wtr.server.Close([]byte(wtr.testData))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wtr.t.Fatalf("%v failed to close: %s", wtr.toolIDs, err)
|
wtr.t.Fatalf("%s failed to close: %s", wtr.suite.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
wireData, err := letter.ToWire()
|
wireData, err := letter.ToWire()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wtr.t.Fatalf("%v failed to serialize to wire: %s", wtr.toolIDs, err)
|
wtr.t.Fatalf("%s failed to serialize to wire: %s", wtr.suite.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case wtr.serverToClient <- wireData:
|
case wtr.serverToClient <- wireData:
|
||||||
default:
|
default:
|
||||||
wtr.t.Fatalf("%v could not send to client", wtr.toolIDs)
|
wtr.t.Fatalf("%s could not send to client", wtr.suite.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Print(".")
|
fmt.Print(".")
|
||||||
|
@ -221,20 +220,20 @@ func (wtr *wireTestRange) clientRecv() {
|
||||||
|
|
||||||
letter, err := LetterFromWire(wireData)
|
letter, err := LetterFromWire(wireData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wtr.t.Fatalf("%v failed to parse initial wired letter: %s", wtr.toolIDs, err)
|
wtr.t.Fatalf("%s failed to parse initial wired letter: %s", wtr.suite.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
origData, err := wtr.client.Open(letter)
|
origData, err := wtr.client.Open(letter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wtr.t.Fatalf("%v failed to open: %s", wtr.toolIDs, err)
|
wtr.t.Fatalf("%s failed to open: %s", wtr.suite.ID, err)
|
||||||
}
|
}
|
||||||
wtr.bytesTransferred += len(origData)
|
wtr.bytesTransferred += len(origData)
|
||||||
|
|
||||||
if string(origData) != wtr.testData {
|
if string(origData) != wtr.testData {
|
||||||
wtr.t.Fatalf("%v testdata mismatch", wtr.toolIDs)
|
wtr.t.Fatalf("%s testdata mismatch", wtr.suite.ID)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
wtr.t.Fatalf("%v could not recv from server", wtr.toolIDs)
|
wtr.t.Fatalf("%s could not recv from server", wtr.suite.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Print(".")
|
fmt.Print(".")
|
||||||
|
|
77
core.go
77
core.go
|
@ -16,7 +16,7 @@ func (s *Session) Close(data []byte) (*Letter, error) { //nolint:gocognit
|
||||||
|
|
||||||
if s.wire == nil || s.wire.msgNo == 0 {
|
if s.wire == nil || s.wire.msgNo == 0 {
|
||||||
letter.Version = 1
|
letter.Version = 1
|
||||||
letter.Tools = s.envelope.Tools
|
letter.SuiteID = s.envelope.SuiteID
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////
|
/////////////////
|
||||||
|
@ -314,6 +314,15 @@ func (s *Session) Open(letter *Letter) ([]byte, error) { //nolint:gocognit,gocyc
|
||||||
|
|
||||||
// Verify verifies signatures of the given letter.
|
// Verify verifies signatures of the given letter.
|
||||||
func (s *Session) Verify(letter *Letter) error {
|
func (s *Session) Verify(letter *Letter) error {
|
||||||
|
|
||||||
|
// debugging:
|
||||||
|
/*
|
||||||
|
fmt.Printf("opening: %+v\n", letter)
|
||||||
|
for _, sig := range letter.Signatures {
|
||||||
|
fmt.Printf("sig: %+v\n", sig)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
if s.wire == nil && letter.Version != 1 {
|
if s.wire == nil && letter.Version != 1 {
|
||||||
return fmt.Errorf("unsupported letter version: %d", letter.Version)
|
return fmt.Errorf("unsupported letter version: %d", letter.Version)
|
||||||
|
@ -323,44 +332,54 @@ func (s *Session) Verify(letter *Letter) error {
|
||||||
// verify
|
// verify
|
||||||
/////////
|
/////////
|
||||||
|
|
||||||
if len(s.signers) == 0 {
|
// TODO: signature verification is run before tool setup. Currently, this is ok, but might change in the future. This might break additional signing algorithms that actually need setup.
|
||||||
return errors.New("letter is not signed")
|
|
||||||
}
|
|
||||||
|
|
||||||
data := letter.Data
|
data := letter.Data
|
||||||
associatedSigningData := letter.compileAssociatedSigningData(nil)
|
|
||||||
|
|
||||||
// run managed signing hashers
|
// build associated data
|
||||||
if s.managedSigningHashers != nil {
|
var associatedData []byte
|
||||||
err = s.feedManagedHashers(s.managedSigningHashers, data, associatedSigningData)
|
if len(s.integratedCiphers) > 0 || len(s.macs) > 0 {
|
||||||
if err != nil {
|
associatedData = letter.compileAssociatedData()
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer s.resetManagedHashers(s.managedSigningHashers)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// run signers
|
// Signature
|
||||||
if len(s.envelope.Senders) != len(letter.Signatures) {
|
if len(s.signers) > 0 {
|
||||||
return errors.New("mismatch regarding available signatures and senders")
|
associatedSigningData := letter.compileAssociatedSigningData(associatedData)
|
||||||
}
|
|
||||||
sigIndex := 0
|
|
||||||
|
|
||||||
for _, tool := range s.signers {
|
// run managed signing hashers
|
||||||
//nolint:scopelint // function is executed immediately within loop
|
if s.managedSigningHashers != nil {
|
||||||
err = s.envelope.LoopSenders(tool.Info().Name, func(signet *Signet) error {
|
err = s.feedManagedHashers(s.managedSigningHashers, data, associatedSigningData)
|
||||||
|
|
||||||
err := tool.Verify(data, associatedSigningData, letter.Signatures[sigIndex].Value, signet)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to verify signature (%s) with ID %s: %s", tool.Info().Name, letter.Signatures[sigIndex].ID, err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
sigIndex++
|
defer s.resetManagedHashers(s.managedSigningHashers)
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// run signers
|
||||||
|
if len(s.envelope.Senders) != len(letter.Signatures) {
|
||||||
|
return errors.New("mismatch regarding available signatures and senders")
|
||||||
|
}
|
||||||
|
sigIndex := 0
|
||||||
|
|
||||||
|
for _, tool := range s.signers {
|
||||||
|
//nolint:scopelint // function is executed immediately within loop
|
||||||
|
err = s.envelope.LoopSenders(tool.Info().Name, func(signet *Signet) error {
|
||||||
|
|
||||||
|
err := tool.Verify(data, associatedSigningData, letter.Signatures[sigIndex].Value, signet)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to verify signature (%s) with ID %s: %s", tool.Info().Name, letter.Signatures[sigIndex].ID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sigIndex++
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return errors.New("no signatures to verify")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
173
core_test.go
173
core_test.go
|
@ -120,16 +120,8 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCoreBasic(t *testing.T) {
|
func TestCoreBasic(t *testing.T) {
|
||||||
// toolsets to test
|
for _, suite := range Suites() {
|
||||||
toolsets := [][]string{
|
testStorage(t, suite)
|
||||||
RecommendedStorageKey,
|
|
||||||
RecommendedStoragePassword,
|
|
||||||
{"HKDF(SHA2-256)", "CHACHA20-POLY1305"},
|
|
||||||
{"PBKDF2-SHA2-256", "HKDF(SHA2-256)", "CHACHA20-POLY1305"},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, toolIDs := range toolsets {
|
|
||||||
testStorage(t, toolIDs)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,10 +129,12 @@ func TestCoreBasic(t *testing.T) {
|
||||||
func TestCoreAllCombinations(t *testing.T) {
|
func TestCoreAllCombinations(t *testing.T) {
|
||||||
// This shall test all tools in all combinations and every tool should be tested when placed before and after every other tool.
|
// This shall test all tools in all combinations and every tool should be tested when placed before and after every other tool.
|
||||||
|
|
||||||
// skip in short tests
|
// skip in short tests and when not running comprehensive
|
||||||
if testing.Short() {
|
if testing.Short() || !runComprehensiveTestsActive {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// run this test with
|
||||||
|
// go test -timeout 10m github.com/safing/jess -v -count=1 -ldflags "-X github.com/safing/jess.RunComprehensiveTests=true" -run ^TestCoreAllCombinations$
|
||||||
|
|
||||||
// add all tools
|
// add all tools
|
||||||
var all []string
|
var all []string
|
||||||
|
@ -193,7 +187,7 @@ func TestCoreAllCombinations(t *testing.T) {
|
||||||
|
|
||||||
// rotate to test before/after differences
|
// rotate to test before/after differences
|
||||||
for i := 0; i < len(testTools); i++ {
|
for i := 0; i < len(testTools); i++ {
|
||||||
detectedInvalid := testStorage(t, testTools)
|
detectedInvalid := testStorage(t, &Suite{Tools: testTools})
|
||||||
combinationsTested++
|
combinationsTested++
|
||||||
if detectedInvalid {
|
if detectedInvalid {
|
||||||
combinationsDetectedInvalid++
|
combinationsDetectedInvalid++
|
||||||
|
@ -207,7 +201,7 @@ func TestCoreAllCombinations(t *testing.T) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// test this order only
|
// test this order only
|
||||||
detectedInvalid := testStorage(t, testTools)
|
detectedInvalid := testStorage(t, &Suite{Tools: testTools})
|
||||||
combinationsTested++
|
combinationsTested++
|
||||||
if detectedInvalid {
|
if detectedInvalid {
|
||||||
combinationsDetectedInvalid++
|
combinationsDetectedInvalid++
|
||||||
|
@ -226,12 +220,12 @@ func TestCoreAllCombinations(t *testing.T) {
|
||||||
t.Logf("of these, %d were successfully detected as invalid", combinationsDetectedInvalid)
|
t.Logf("of these, %d were successfully detected as invalid", combinationsDetectedInvalid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testStorage(t *testing.T, toolIDs []string) (detectedInvalid bool) {
|
func testStorage(t *testing.T, suite *Suite) (detectedInvalid bool) {
|
||||||
// t.Logf("testing storage with %v", toolIDs)
|
// t.Logf("testing storage with %s", suite.ID)
|
||||||
|
|
||||||
e, err := setupEnvelopeAndTrustStore(t, toolIDs)
|
e, err := setupEnvelopeAndTrustStore(t, suite)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tErrorf(t, "%v failed: %s", toolIDs, err)
|
tErrorf(t, "%s failed: %s", suite.ID, err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if e == nil {
|
if e == nil {
|
||||||
|
@ -242,122 +236,72 @@ func testStorage(t *testing.T, toolIDs []string) (detectedInvalid bool) {
|
||||||
|
|
||||||
s, err := e.Correspondence(testTrustStore)
|
s, err := e.Correspondence(testTrustStore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tErrorf(t, "%v failed to init session (1): %s", toolIDs, err)
|
tErrorf(t, "%s failed to init session (1): %s", suite.ID, err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
letter, err := s.Close([]byte(testData1))
|
letter, err := s.Close([]byte(testData1))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tErrorf(t, "%v failed to close (1): %s", toolIDs, err)
|
tErrorf(t, "%s failed to close (1): %s", suite.ID, err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
msg, err := letter.ToJSON()
|
msg, err := letter.ToJSON()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tErrorf(t, "%v failed to json encode (1): %s", toolIDs, err)
|
tErrorf(t, "%s failed to json encode (1): %s", suite.ID, err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// test 2: open from session
|
// test 2: open
|
||||||
|
|
||||||
letter2, err := LetterFromJSON(msg)
|
letter2, err := LetterFromJSON(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tErrorf(t, "%v failed to json decode (2): %s", toolIDs, err)
|
tErrorf(t, "%s failed to json decode (2): %s", suite.ID, err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
origData2, err := s.Open(letter2)
|
origData2, err := letter2.Open(e.suite.Provides, testTrustStore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tErrorf(t, "%v failed to open (2): %s", toolIDs, err)
|
tErrorf(t, "%s failed to open (2): %s", suite.ID, err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if string(origData2) != testData1 {
|
if string(origData2) != testData1 {
|
||||||
tErrorf(t, "%v original data mismatch (2): %s", toolIDs, string(origData2))
|
tErrorf(t, "%s original data mismatch (2): %s", suite.ID, string(origData2))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(letter2.Signatures) > 0 {
|
// test 2.1: verify
|
||||||
err = s.Verify(letter2)
|
|
||||||
if err != nil {
|
|
||||||
tErrorf(t, "%v failed to verify (2): %s", toolIDs, err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// extended tests
|
|
||||||
// only run for toolsets greater than 3 if we comprehensive testing is on
|
|
||||||
// for these tests, it is enough if every tool is tested once
|
|
||||||
if len(toolIDs) > 3 && RunComprehensiveTests != "true" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// test 2.1: open again to check if reset after opening works
|
|
||||||
|
|
||||||
letter21, err := LetterFromJSON(msg)
|
letter21, err := LetterFromJSON(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tErrorf(t, "%v failed to json decode (2.1): %s", toolIDs, err)
|
tErrorf(t, "%s failed to json decode (2): %s", suite.ID, err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
origData21, err := s.Open(letter21)
|
if len(letter21.Signatures) > 0 {
|
||||||
if err != nil {
|
err = letter21.Verify(e.suite.Provides, testTrustStore)
|
||||||
tErrorf(t, "%v failed to open (2.1): %s", toolIDs, err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if string(origData21) != testData1 {
|
|
||||||
tErrorf(t, "%v original data mismatch (2.1): %s", toolIDs, string(origData21))
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// test 2.2: close and open again to check if reset after closing works
|
|
||||||
|
|
||||||
letter22, err := s.Close([]byte(testData1))
|
|
||||||
if err != nil {
|
|
||||||
tErrorf(t, "%v failed to close (2.2): %s", toolIDs, err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
origData22, err := s.Open(letter22)
|
|
||||||
if err != nil {
|
|
||||||
tErrorf(t, "%v failed to open (2.2): %s", toolIDs, err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if string(origData22) != testData1 {
|
|
||||||
tErrorf(t, "%v original data mismatch (2.2): %s", toolIDs, string(origData22))
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// test 3: open from letter
|
|
||||||
|
|
||||||
// FIXME - other improvements broke these tests, pausing them
|
|
||||||
/*
|
|
||||||
letter3, err := LetterFromJSON(msg)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tErrorf(t, "%v failed to json decode (3): %s", toolIDs, err)
|
tErrorf(t, "%s failed to verify (2): %s", suite.ID, err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
}
|
||||||
origData3, err := letter3.Open(nil, testTrustStore)
|
|
||||||
if err != nil {
|
|
||||||
tErrorf(t, "%v failed to open (3): %s", toolIDs, err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if string(origData3) != testData1 {
|
|
||||||
tErrorf(t, "%v original data mismatch (3): %s", toolIDs, string(origData3))
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:gocognit,gocyclo
|
//nolint:gocognit,gocyclo
|
||||||
func setupEnvelopeAndTrustStore(t *testing.T, toolIDs []string) (*Envelope, error) {
|
func setupEnvelopeAndTrustStore(t *testing.T, suite *Suite) (*Envelope, error) {
|
||||||
|
// check if suite is registered
|
||||||
|
if suite.ID == "" {
|
||||||
|
// register as test suite
|
||||||
|
suite.ID = fmt.Sprintf("__unit_test_suite__" + strings.Join(suite.Tools, "__"))
|
||||||
|
registerSuite(suite)
|
||||||
|
}
|
||||||
|
|
||||||
// create envelope baseline
|
// create envelope baseline
|
||||||
e := &Envelope{
|
e := &Envelope{
|
||||||
Tools: toolIDs,
|
SuiteID: suite.ID,
|
||||||
requirements: newEmptyRequirements(),
|
suite: suite,
|
||||||
}
|
}
|
||||||
|
|
||||||
// check vars
|
// check vars
|
||||||
|
@ -366,7 +310,7 @@ func setupEnvelopeAndTrustStore(t *testing.T, toolIDs []string) (*Envelope, erro
|
||||||
asyncKeyEstablishmentPresent := false
|
asyncKeyEstablishmentPresent := false
|
||||||
|
|
||||||
// process tools and setup envelope
|
// process tools and setup envelope
|
||||||
for _, toolID := range e.Tools {
|
for _, toolID := range e.suite.Tools {
|
||||||
|
|
||||||
// remove hasher argument for now
|
// remove hasher argument for now
|
||||||
if strings.Contains(toolID, "(") {
|
if strings.Contains(toolID, "(") {
|
||||||
|
@ -389,7 +333,7 @@ func setupEnvelopeAndTrustStore(t *testing.T, toolIDs []string) (*Envelope, erro
|
||||||
e.Secrets = append(e.Secrets, pw)
|
e.Secrets = append(e.Secrets, pw)
|
||||||
|
|
||||||
// add a second one!
|
// add a second one!
|
||||||
if len(toolIDs) <= 2 {
|
if len(suite.Tools) <= 2 {
|
||||||
pw1, err := getOrMakeSignet(t, nil, false, "test-pw-2")
|
pw1, err := getOrMakeSignet(t, nil, false, "test-pw-2")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -422,66 +366,69 @@ func setupEnvelopeAndTrustStore(t *testing.T, toolIDs []string) (*Envelope, erro
|
||||||
passDerPresent = true
|
passDerPresent = true
|
||||||
// add passderivation requirements later, as it is a bit special
|
// add passderivation requirements later, as it is a bit special
|
||||||
case tools.PurposeKeyExchange:
|
case tools.PurposeKeyExchange:
|
||||||
e.requirements.Add(RecipientAuthentication)
|
e.suite.Provides.Add(RecipientAuthentication)
|
||||||
case tools.PurposeKeyEncapsulation:
|
case tools.PurposeKeyEncapsulation:
|
||||||
e.requirements.Add(RecipientAuthentication)
|
e.suite.Provides.Add(RecipientAuthentication)
|
||||||
case tools.PurposeSigning:
|
case tools.PurposeSigning:
|
||||||
e.requirements.Add(SenderAuthentication)
|
e.suite.Provides.Add(SenderAuthentication)
|
||||||
case tools.PurposeIntegratedCipher:
|
case tools.PurposeIntegratedCipher:
|
||||||
e.requirements.Add(Confidentiality)
|
e.suite.Provides.Add(Confidentiality)
|
||||||
e.requirements.Add(Integrity)
|
e.suite.Provides.Add(Integrity)
|
||||||
case tools.PurposeCipher:
|
case tools.PurposeCipher:
|
||||||
e.requirements.Add(Confidentiality)
|
e.suite.Provides.Add(Confidentiality)
|
||||||
case tools.PurposeMAC:
|
case tools.PurposeMAC:
|
||||||
e.requirements.Add(Integrity)
|
e.suite.Provides.Add(Integrity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if invalid: test if toolset is recognized as invalid
|
// if invalid: test if toolset is recognized as invalid
|
||||||
|
|
||||||
// no requirements -> only "meta" tools (kdf, pass derivation)
|
// no requirements -> only "meta" tools (kdf, pass derivation)
|
||||||
if e.requirements.Empty() {
|
if e.suite.Provides.Empty() {
|
||||||
return nil, testInvalidToolset(e, "there are only meta tools in toolset")
|
return nil, testInvalidToolset(e, "there are only meta tools in toolset")
|
||||||
}
|
}
|
||||||
|
|
||||||
// recipient auth, but no confidentiality? nope.
|
// recipient auth, but no confidentiality? nope.
|
||||||
if e.requirements.Has(RecipientAuthentication) &&
|
if e.suite.Provides.Has(RecipientAuthentication) &&
|
||||||
!e.requirements.Has(Confidentiality) {
|
!e.suite.Provides.Has(Confidentiality) {
|
||||||
return nil, testInvalidToolset(e, "authenticating the recipient without using confidentiality does not make sense")
|
return nil, testInvalidToolset(e, "authenticating the recipient without using confidentiality does not make sense")
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if we are missing key derivation - this is only ok if we are merely signing
|
// check if we are missing key derivation - this is only ok if we are merely signing
|
||||||
if !keyDerPresent &&
|
if !keyDerPresent &&
|
||||||
(len(e.requirements.all) != 1 ||
|
(len(e.suite.Provides.all) != 1 ||
|
||||||
!e.requirements.Has(SenderAuthentication)) {
|
!e.suite.Provides.Has(SenderAuthentication)) {
|
||||||
return nil, testInvalidToolset(e, "omitting a key derivation tool is only allowed when merely signing")
|
return nil, testInvalidToolset(e, "omitting a key derivation tool is only allowed when merely signing")
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if we have key derivation, but not need it
|
// check if we have key derivation, but not need it
|
||||||
if keyDerPresent &&
|
if keyDerPresent &&
|
||||||
(!e.requirements.Has(Confidentiality) &&
|
(!e.suite.Provides.Has(Confidentiality) &&
|
||||||
!e.requirements.Has(Integrity)) {
|
!e.suite.Provides.Has(Integrity)) {
|
||||||
return nil, testInvalidToolset(e, "a key derivation tool was specified, albeit none is needed")
|
return nil, testInvalidToolset(e, "a key derivation tool was specified, albeit none is needed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// add passderivation here, as to easier handle the other cases
|
// add passderivation here, as to easier handle the other cases
|
||||||
if passDerPresent {
|
if passDerPresent {
|
||||||
e.requirements.Add(SenderAuthentication)
|
e.suite.Provides.Add(SenderAuthentication)
|
||||||
e.requirements.Add(RecipientAuthentication)
|
e.suite.Provides.Add(RecipientAuthentication)
|
||||||
|
|
||||||
// need Confidentiality for this to make sense
|
// need Confidentiality for this to make sense
|
||||||
if !e.requirements.Has(Confidentiality) {
|
if !e.suite.Provides.Has(Confidentiality) {
|
||||||
return nil, testInvalidToolset(e, "using a password without confidentiality does not make sense")
|
return nil, testInvalidToolset(e, "using a password without confidentiality does not make sense")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.requirements.Has(Confidentiality) &&
|
if e.suite.Provides.Has(Confidentiality) &&
|
||||||
!e.requirements.Has(Integrity) {
|
!e.suite.Provides.Has(Integrity) {
|
||||||
return nil, testInvalidToolset(e, "having confidentiality without integrity does not make sense")
|
return nil, testInvalidToolset(e, "having confidentiality without integrity does not make sense")
|
||||||
}
|
}
|
||||||
|
|
||||||
// add static key if needed
|
// add static key if needed
|
||||||
if !asyncKeyEstablishmentPresent && !passDerPresent && keyDerPresent {
|
if !asyncKeyEstablishmentPresent && !passDerPresent && keyDerPresent {
|
||||||
|
e.suite.Provides.Add(SenderAuthentication)
|
||||||
|
e.suite.Provides.Add(RecipientAuthentication)
|
||||||
|
|
||||||
key, err := getOrMakeSignet(t, nil, false, "test-key-1")
|
key, err := getOrMakeSignet(t, nil, false, "test-key-1")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -489,7 +436,7 @@ func setupEnvelopeAndTrustStore(t *testing.T, toolIDs []string) (*Envelope, erro
|
||||||
e.Secrets = append(e.Secrets, key)
|
e.Secrets = append(e.Secrets, key)
|
||||||
|
|
||||||
// add a second one!
|
// add a second one!
|
||||||
if len(toolIDs) <= 2 {
|
if len(suite.Tools) <= 2 {
|
||||||
key2, err := getOrMakeSignet(t, nil, false, "test-key-2")
|
key2, err := getOrMakeSignet(t, nil, false, "test-key-2")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -3,10 +3,10 @@ package jess
|
||||||
var (
|
var (
|
||||||
// must be var in order decrease for testing for better speed
|
// must be var in order decrease for testing for better speed
|
||||||
|
|
||||||
defaultSecurityLevel = 256
|
defaultSecurityLevel = 128
|
||||||
minimumSecurityLevel = 0
|
minimumSecurityLevel = 0
|
||||||
|
|
||||||
defaultSymmetricKeySize = 32
|
defaultSymmetricKeySize = 16
|
||||||
minimumSymmetricKeySize = 0
|
minimumSymmetricKeySize = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
112
envelope.go
112
envelope.go
|
@ -9,7 +9,8 @@ import (
|
||||||
type Envelope struct { //nolint:maligned // TODO
|
type Envelope struct { //nolint:maligned // TODO
|
||||||
Version uint8
|
Version uint8
|
||||||
Name string
|
Name string
|
||||||
Tools []string
|
SuiteID string
|
||||||
|
suite *Suite
|
||||||
|
|
||||||
// Secret keys and passwords
|
// Secret keys and passwords
|
||||||
Secrets []*Signet
|
Secrets []*Signet
|
||||||
|
@ -26,11 +27,10 @@ type Envelope struct { //nolint:maligned // TODO
|
||||||
|
|
||||||
// For users, envelopes describe how a letter is closed.
|
// For users, envelopes describe how a letter is closed.
|
||||||
// Therefore Secrets and Senders always refer to private keys and Recipients to public keys in that context.
|
// Therefore Secrets and Senders always refer to private keys and Recipients to public keys in that context.
|
||||||
// These distictions are important in order for the user to easily and confidently distinguish what is going to happen. Think of it as "human security".
|
// These distinctions are important in order for the user to easily and confidently distinguish what is going to happen. Think of it as "human security".
|
||||||
|
|
||||||
MinimumSecurityLevel int
|
// SecurityLevel is the security level of the envelope when it was created
|
||||||
No string
|
SecurityLevel int
|
||||||
requirements *Requirements
|
|
||||||
|
|
||||||
// flag to signify if envelope is used for opening
|
// flag to signify if envelope is used for opening
|
||||||
opening bool
|
opening bool
|
||||||
|
@ -39,10 +39,8 @@ type Envelope struct { //nolint:maligned // TODO
|
||||||
// NewUnconfiguredEnvelope returns an unconfigured, but slightly initialized envelope.
|
// NewUnconfiguredEnvelope returns an unconfigured, but slightly initialized envelope.
|
||||||
func NewUnconfiguredEnvelope() *Envelope {
|
func NewUnconfiguredEnvelope() *Envelope {
|
||||||
e := &Envelope{
|
e := &Envelope{
|
||||||
Version: 1,
|
Version: 1,
|
||||||
requirements: NewRequirements(),
|
|
||||||
}
|
}
|
||||||
e.SerializeRequirements()
|
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,14 +50,17 @@ func (e *Envelope) Correspondence(trustStore TrustStore) (*Session, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Envelope) initCorrespondence(trustStore TrustStore, verifying bool) (*Session, error) {
|
func (e *Envelope) initCorrespondence(trustStore TrustStore, verifying bool) (*Session, error) {
|
||||||
err := e.LoadRequirements()
|
err := e.LoadSuite()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:gocritic // TODO: see below
|
||||||
if verifying {
|
if verifying {
|
||||||
// prep sender signets only
|
// TODO: prep sender signets only
|
||||||
err = e.prepSignets(e.Senders, e.opening, trustStore)
|
// TODO: for this to work, newSession needs to only check verification related things
|
||||||
|
// err = e.prepSignets(e.Senders, e.opening, trustStore)
|
||||||
|
err = e.PrepareSignets(trustStore)
|
||||||
} else {
|
} else {
|
||||||
// prep all signets
|
// prep all signets
|
||||||
err = e.PrepareSignets(trustStore)
|
err = e.PrepareSignets(trustStore)
|
||||||
|
@ -92,82 +93,27 @@ func (e *Envelope) Check(trustStore TrustStore) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// NoRecipientAuth removes the requirement to authenticate the recipient.
|
// Suite returns the loaded suite.
|
||||||
func (e *Envelope) NoRecipientAuth() *Envelope {
|
func (e *Envelope) Suite() *Suite {
|
||||||
if e.requirements == nil {
|
return e.suite
|
||||||
e.requirements = NewRequirements()
|
|
||||||
}
|
|
||||||
|
|
||||||
e.requirements.Remove(RecipientAuthentication)
|
|
||||||
return e
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NoSenderAuth removes the requirement to authenticate the sender.
|
// LoadSuite loads the suite specified in the envelope.
|
||||||
func (e *Envelope) NoSenderAuth() *Envelope {
|
func (e *Envelope) LoadSuite() error {
|
||||||
if e.requirements == nil {
|
if e.suite == nil {
|
||||||
e.requirements = NewRequirements()
|
suite, ok := GetSuite(e.SuiteID)
|
||||||
}
|
if !ok {
|
||||||
|
return fmt.Errorf("suite %s does not exist", e.SuiteID)
|
||||||
e.requirements.Remove(SenderAuthentication)
|
|
||||||
e.SerializeRequirements()
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
// NoConfidentiality removes the requirement to provide confidentiality.
|
|
||||||
func (e *Envelope) NoConfidentiality() *Envelope {
|
|
||||||
if e.requirements == nil {
|
|
||||||
e.requirements = NewRequirements()
|
|
||||||
}
|
|
||||||
|
|
||||||
e.requirements.Remove(Confidentiality)
|
|
||||||
e.SerializeRequirements()
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
// NoIntegrity removes the requirement to provide integrity.
|
|
||||||
func (e *Envelope) NoIntegrity() *Envelope {
|
|
||||||
if e.requirements == nil {
|
|
||||||
e.requirements = NewRequirements()
|
|
||||||
}
|
|
||||||
|
|
||||||
e.requirements.Remove(Integrity)
|
|
||||||
e.SerializeRequirements()
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unsafe removes all requirements.
|
|
||||||
func (e *Envelope) Unsafe() *Envelope {
|
|
||||||
e.requirements = &Requirements{}
|
|
||||||
e.SerializeRequirements()
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
// Requirements returns the required requirements.
|
|
||||||
func (e *Envelope) Requirements() *Requirements {
|
|
||||||
return e.requirements
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetRequirements sets new requirements.
|
|
||||||
func (e *Envelope) SetRequirements(requirements *Requirements) {
|
|
||||||
e.requirements = requirements
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadRequirements loads the required requirements from the struct's exposed negated "No" specification.
|
|
||||||
func (e *Envelope) LoadRequirements() error {
|
|
||||||
if e.requirements == nil {
|
|
||||||
attrs, err := ParseRequirementsFromNoSpec(e.No)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
e.suite = suite
|
||||||
e.requirements = attrs
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SerializeRequirements saves the requirement requirements in the struct's exposed negated "No" specification.
|
// ReloadSuite forces reloading the suite specified in the envelope.
|
||||||
func (e *Envelope) SerializeRequirements() {
|
func (e *Envelope) ReloadSuite() error {
|
||||||
e.No = e.requirements.SerializeToNoSpec()
|
e.suite = nil
|
||||||
|
return e.LoadSuite()
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoopSecrets loops over all secrets of the given scheme.
|
// LoopSecrets loops over all secrets of the given scheme.
|
||||||
|
@ -233,12 +179,16 @@ func (e *Envelope) prepSignets(signets []*Signet, recipients bool, storage Trust
|
||||||
// load from storage
|
// load from storage
|
||||||
if len(signet.Key) == 0 {
|
if len(signet.Key) == 0 {
|
||||||
if signet.Scheme == SignetSchemePassword {
|
if signet.Scheme == SignetSchemePassword {
|
||||||
err := fillPassword(signet, !recipients, storage, e.MinimumSecurityLevel)
|
err := fillPassword(signet, !recipients, storage, e.suite.SecurityLevel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(`failed to get password for "%s": %s`, signet.ID, err)
|
return fmt.Errorf(`failed to get password for "%s": %s`, signet.ID, err)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
// keys are _always_ signets
|
||||||
|
if signet.Scheme == SignetSchemeKey {
|
||||||
|
recipients = false
|
||||||
|
}
|
||||||
|
|
||||||
// signet is referrer
|
// signet is referrer
|
||||||
if len(signet.ID) == 0 {
|
if len(signet.ID) == 0 {
|
||||||
|
|
|
@ -15,9 +15,7 @@ import (
|
||||||
- 2: Sending Keys
|
- 2: Sending Keys
|
||||||
- 4: Apply Keys
|
- 4: Apply Keys
|
||||||
- Version: varint (if Setup Msg)
|
- Version: varint (if Setup Msg)
|
||||||
- Tools: (if Setup Msg)
|
- SuiteID: byte block (if Setup Msg)
|
||||||
- Amount: varint
|
|
||||||
- Names: byte blocks
|
|
||||||
- Keys:
|
- Keys:
|
||||||
- Amount: varint
|
- Amount: varint
|
||||||
- IDs/Values: byte blocks
|
- IDs/Values: byte blocks
|
||||||
|
@ -58,13 +56,8 @@ func (letter *Letter) ToWire() (*container.Container, error) {
|
||||||
// Version: varint (if Setup Msg)
|
// Version: varint (if Setup Msg)
|
||||||
c.AppendNumber(uint64(letter.Version))
|
c.AppendNumber(uint64(letter.Version))
|
||||||
|
|
||||||
// Tools: (if Setup Msg)
|
// SuiteID: byte block (if Setup Msg)
|
||||||
// - Amount: varint
|
c.AppendAsBlock([]byte(letter.SuiteID))
|
||||||
// - Names: byte blocks
|
|
||||||
c.AppendInt(len(letter.Tools))
|
|
||||||
for _, toolName := range letter.Tools {
|
|
||||||
c.AppendAsBlock([]byte(toolName))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(letter.Keys) > 0 {
|
if len(letter.Keys) > 0 {
|
||||||
|
@ -136,21 +129,12 @@ func LetterFromWire(c *container.Container) (*Letter, error) {
|
||||||
}
|
}
|
||||||
letter.Version = n
|
letter.Version = n
|
||||||
|
|
||||||
// Tools: (if Setup Msg)
|
// SuiteID: byte block (if Setup Msg)
|
||||||
// - Amount: varint
|
suiteID, err := c.GetNextBlock()
|
||||||
// - Names: byte blocks
|
|
||||||
n, err = c.GetNextN8()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
letter.Tools = make([]string, n)
|
letter.SuiteID = string(suiteID)
|
||||||
for i := 0; i < len(letter.Tools); i++ {
|
|
||||||
toolName, err := c.GetNextBlock()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
letter.Tools[i] = string(toolName)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if sendingKeys {
|
if sendingKeys {
|
||||||
|
|
55
letter.go
55
letter.go
|
@ -17,8 +17,8 @@ import (
|
||||||
|
|
||||||
// Letter is the data format for encrypted data at rest or in transit.
|
// Letter is the data format for encrypted data at rest or in transit.
|
||||||
type Letter struct { //nolint:maligned // TODO
|
type Letter struct { //nolint:maligned // TODO
|
||||||
Version uint8 // signed, MAC'd (may not exist when wired)
|
Version uint8 // signed, MAC'd (may not exist when wired)
|
||||||
Tools []string // signed, MAC'd (may not exist when wired)
|
SuiteID string // signed, MAC'd (may not exist when wired)
|
||||||
|
|
||||||
Nonce []byte // signed, MAC'd
|
Nonce []byte // signed, MAC'd
|
||||||
Keys []*Seal `json:",omitempty"` // signed, MAC'd
|
Keys []*Seal `json:",omitempty"` // signed, MAC'd
|
||||||
|
@ -45,18 +45,34 @@ type Seal struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Envelope returns an envelope built from the letter, configured for opening it.
|
// Envelope returns an envelope built from the letter, configured for opening it.
|
||||||
func (letter *Letter) Envelope() (*Envelope, error) {
|
func (letter *Letter) Envelope(requirements *Requirements) (*Envelope, error) {
|
||||||
|
// basic checks
|
||||||
if letter.Version == 0 {
|
if letter.Version == 0 {
|
||||||
return nil, fmt.Errorf("letter does not specify version")
|
return nil, fmt.Errorf("letter does not specify version")
|
||||||
}
|
}
|
||||||
if len(letter.Tools) == 0 {
|
if len(letter.SuiteID) == 0 {
|
||||||
return nil, fmt.Errorf("letter does not specify any tools")
|
return nil, fmt.Errorf("letter does not specify a suite")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create envelope
|
||||||
e := &Envelope{
|
e := &Envelope{
|
||||||
Version: letter.Version,
|
Version: letter.Version,
|
||||||
Tools: letter.Tools,
|
SuiteID: letter.SuiteID,
|
||||||
requirements: newEmptyRequirements(),
|
}
|
||||||
|
|
||||||
|
// get and check suite
|
||||||
|
err := e.LoadSuite()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// default to full requirements
|
||||||
|
if requirements == nil {
|
||||||
|
requirements = NewRequirements()
|
||||||
|
}
|
||||||
|
// check suite against requirements
|
||||||
|
err = e.suite.Provides.CheckComplianceTo(requirements)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, seal := range letter.Keys {
|
for _, seal := range letter.Keys {
|
||||||
|
@ -91,13 +107,10 @@ func (letter *Letter) Envelope() (*Envelope, error) {
|
||||||
|
|
||||||
// Open creates a session and opens the letter in one step.
|
// Open creates a session and opens the letter in one step.
|
||||||
func (letter *Letter) Open(requirements *Requirements, trustStore TrustStore) ([]byte, error) {
|
func (letter *Letter) Open(requirements *Requirements, trustStore TrustStore) ([]byte, error) {
|
||||||
e, err := letter.Envelope()
|
e, err := letter.Envelope(requirements)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if requirements != nil {
|
|
||||||
e.requirements = requirements
|
|
||||||
}
|
|
||||||
|
|
||||||
s, err := e.Correspondence(trustStore)
|
s, err := e.Correspondence(trustStore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -109,13 +122,10 @@ func (letter *Letter) Open(requirements *Requirements, trustStore TrustStore) ([
|
||||||
|
|
||||||
// Verify creates a session and verifies the letter in one step.
|
// Verify creates a session and verifies the letter in one step.
|
||||||
func (letter *Letter) Verify(requirements *Requirements, trustStore TrustStore) error {
|
func (letter *Letter) Verify(requirements *Requirements, trustStore TrustStore) error {
|
||||||
e, err := letter.Envelope()
|
e, err := letter.Envelope(requirements)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if requirements != nil {
|
|
||||||
e.requirements = requirements
|
|
||||||
}
|
|
||||||
|
|
||||||
s, err := e.initCorrespondence(trustStore, true)
|
s, err := e.initCorrespondence(trustStore, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -127,7 +137,7 @@ func (letter *Letter) Verify(requirements *Requirements, trustStore TrustStore)
|
||||||
|
|
||||||
// WireCorrespondence creates a wire session (communication over a network connection) from a letter.
|
// WireCorrespondence creates a wire session (communication over a network connection) from a letter.
|
||||||
func (letter *Letter) WireCorrespondence(trustStore TrustStore) (*Session, error) {
|
func (letter *Letter) WireCorrespondence(trustStore TrustStore) (*Session, error) {
|
||||||
e, err := letter.Envelope()
|
e, err := letter.Envelope(NewRequirements().Remove(SenderAuthentication))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -179,7 +189,7 @@ const (
|
||||||
// These IDs MUST NOT CHANGE
|
// These IDs MUST NOT CHANGE
|
||||||
|
|
||||||
fieldIDLetterVersion uint64 = 1 // signed, MAC'd (may not exist when wired)
|
fieldIDLetterVersion uint64 = 1 // signed, MAC'd (may not exist when wired)
|
||||||
fieldIDLetterTools uint64 = 2 // signed, MAC'd (may not exist when wired)
|
fieldIDLetterSuiteID uint64 = 2 // signed, MAC'd (may not exist when wired)
|
||||||
fieldIDLetterNonce uint64 = 3 // signed, MAC'd
|
fieldIDLetterNonce uint64 = 3 // signed, MAC'd
|
||||||
fieldIDLetterKeys uint64 = 4 // signed, MAC'd
|
fieldIDLetterKeys uint64 = 4 // signed, MAC'd
|
||||||
fieldIDLetterMac uint64 = 5 // signed
|
fieldIDLetterMac uint64 = 5 // signed
|
||||||
|
@ -199,12 +209,9 @@ func (letter *Letter) compileAssociatedData() []byte {
|
||||||
c.AppendNumber(fieldIDLetterVersion) // append field ID
|
c.AppendNumber(fieldIDLetterVersion) // append field ID
|
||||||
c.AppendNumber(uint64(letter.Version))
|
c.AppendNumber(uint64(letter.Version))
|
||||||
}
|
}
|
||||||
if len(letter.Tools) > 0 {
|
if len(letter.SuiteID) > 0 {
|
||||||
c.AppendNumber(fieldIDLetterTools) // append field ID
|
c.AppendNumber(fieldIDLetterSuiteID) // append field ID
|
||||||
c.AppendInt(len(letter.Tools)) // append number of tools
|
c.AppendAsBlock([]byte(letter.SuiteID)) // append field content with length
|
||||||
for _, toolID := range letter.Tools {
|
|
||||||
c.AppendAsBlock([]byte(toolID)) // append field content with length
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if len(letter.Nonce) > 0 {
|
if len(letter.Nonce) > 0 {
|
||||||
c.AppendNumber(fieldIDLetterNonce) // append field ID
|
c.AppendNumber(fieldIDLetterNonce) // append field ID
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
func TestSerialization(t *testing.T) {
|
func TestSerialization(t *testing.T) {
|
||||||
subject := &Letter{
|
subject := &Letter{
|
||||||
Version: 1,
|
Version: 1,
|
||||||
Tools: RecommendedNetwork,
|
SuiteID: SuiteComplete,
|
||||||
Keys: []*Seal{
|
Keys: []*Seal{
|
||||||
{ID: "a"},
|
{ID: "a"},
|
||||||
{ID: "b"},
|
{ID: "b"},
|
||||||
|
@ -25,7 +25,7 @@ func TestSerialization(t *testing.T) {
|
||||||
testSerialize(t, subject, true)
|
testSerialize(t, subject, true)
|
||||||
|
|
||||||
subject.Version = 0
|
subject.Version = 0
|
||||||
subject.Tools = nil
|
subject.SuiteID = ""
|
||||||
testSerialize(t, subject, true)
|
testSerialize(t, subject, true)
|
||||||
|
|
||||||
subject.ApplyKeys = false
|
subject.ApplyKeys = false
|
||||||
|
|
|
@ -2,6 +2,24 @@ package jess
|
||||||
|
|
||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
SetPasswordCallbacks(
|
||||||
|
func(signet *Signet, minSecurityLevel int) error {
|
||||||
|
return getTestPassword(signet)
|
||||||
|
},
|
||||||
|
getTestPassword,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTestPassword(signet *Signet) error {
|
||||||
|
pwSignet, err := testTrustStore.GetSignet(signet.ID, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
signet.Key = pwSignet.Key
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func TestCalculatePasswordSecurityLevel(t *testing.T) {
|
func TestCalculatePasswordSecurityLevel(t *testing.T) {
|
||||||
// basic weak
|
// basic weak
|
||||||
testPWSL(t, "asdf", -1)
|
testPWSL(t, "asdf", -1)
|
||||||
|
@ -13,62 +31,62 @@ func TestCalculatePasswordSecurityLevel(t *testing.T) {
|
||||||
testPWSL(t, "aaaaaaaaAAAAAAAA00000000********", -1)
|
testPWSL(t, "aaaaaaaaAAAAAAAA00000000********", -1)
|
||||||
|
|
||||||
// chars only
|
// chars only
|
||||||
testPWSL(t, "AVWHBwmF", 58)
|
testPWSL(t, "AVWHBwmF", 64)
|
||||||
testPWSL(t, "AVWHBwmFGt", 70)
|
testPWSL(t, "AVWHBwmFGt", 76)
|
||||||
testPWSL(t, "AVWHBwmFGtLM", 81)
|
testPWSL(t, "AVWHBwmFGtLM", 87)
|
||||||
testPWSL(t, "AVWHBwmFGtLMGh", 93)
|
testPWSL(t, "AVWHBwmFGtLMGh", 98)
|
||||||
testPWSL(t, "AVWHBwmFGtLMGhYf", 104)
|
testPWSL(t, "AVWHBwmFGtLMGhYf", 110)
|
||||||
testPWSL(t, "AVWHBwmFGtLMGhYfPkcyawfmZXRTQdxs", 195)
|
testPWSL(t, "AVWHBwmFGtLMGhYfPkcyawfmZXRTQdxs", 201)
|
||||||
|
|
||||||
// with number
|
// with number
|
||||||
testPWSL(t, "AVWHBwm1", 60)
|
testPWSL(t, "AVWHBwm1", 66)
|
||||||
testPWSL(t, "AVWHBwmFG1", 72)
|
testPWSL(t, "AVWHBwmFG1", 78)
|
||||||
testPWSL(t, "AVWHBwmFGtL1", 84)
|
testPWSL(t, "AVWHBwmFGtL1", 90)
|
||||||
testPWSL(t, "AVWHBwmFGtLMG1", 96)
|
testPWSL(t, "AVWHBwmFGtLMG1", 102)
|
||||||
testPWSL(t, "AVWHBwmFGtLMGhY1", 108)
|
testPWSL(t, "AVWHBwmFGtLMGhY1", 114)
|
||||||
testPWSL(t, "AVWHBwmFGtLMGhYfPkcyawfmZXRTQdx1", 203)
|
testPWSL(t, "AVWHBwmFGtLMGhYfPkcyawfmZXRTQdx1", 209)
|
||||||
|
|
||||||
// with number and special
|
// with number and special
|
||||||
testPWSL(t, "AVWHBw1_", 61)
|
testPWSL(t, "AVWHBw1_", 67)
|
||||||
testPWSL(t, "AVWHBwmF1_", 73)
|
testPWSL(t, "AVWHBwmF1_", 79)
|
||||||
testPWSL(t, "AVWHBwmFGt1_", 86)
|
testPWSL(t, "AVWHBwmFGt1_", 91)
|
||||||
testPWSL(t, "AVWHBwmFGtLM1_", 98)
|
testPWSL(t, "AVWHBwmFGtLM1_", 103)
|
||||||
testPWSL(t, "AVWHBwmFGtLMGh1_", 110)
|
testPWSL(t, "AVWHBwmFGtLMGh1_", 116)
|
||||||
testPWSL(t, "AVWHBwmFGtLMGhYfPkcyawfmZXRTQd1_", 207)
|
testPWSL(t, "AVWHBwmFGtLMGhYfPkcyawfmZXRTQd1_", 213)
|
||||||
|
|
||||||
// with number and more special
|
// with number and more special
|
||||||
testPWSL(t, "AVWHBw1*", 65)
|
testPWSL(t, "AVWHBw1*", 70)
|
||||||
testPWSL(t, "AVWHBwmF1*", 78)
|
testPWSL(t, "AVWHBwmF1*", 83)
|
||||||
testPWSL(t, "AVWHBwmFGt1*", 91)
|
testPWSL(t, "AVWHBwmFGt1*", 96)
|
||||||
testPWSL(t, "AVWHBwmFGtLM1*", 104)
|
testPWSL(t, "AVWHBwmFGtLM1*", 109)
|
||||||
testPWSL(t, "AVWHBwmFGtLMGh1*", 117)
|
testPWSL(t, "AVWHBwmFGtLMGh1*", 122)
|
||||||
testPWSL(t, "AVWHBwmFGtLMGhYfPkcyawfmZXRTQd1*", 221)
|
testPWSL(t, "AVWHBwmFGtLMGhYfPkcyawfmZXRTQd1*", 226)
|
||||||
|
|
||||||
// created, strong
|
// created, strong
|
||||||
|
|
||||||
// "Schneier scheme"
|
// "Schneier scheme"
|
||||||
// source: https://www.schneier.com/blog/archives/2014/03/choosing_secure_1.html
|
// source: https://www.schneier.com/blog/archives/2014/03/choosing_secure_1.html
|
||||||
testPWSL(t, "WIw7,mstmsritt...", 116)
|
testPWSL(t, "WIw7,mstmsritt...", 122)
|
||||||
testPWSL(t, "Wow...doestcst", 94)
|
testPWSL(t, "Wow...doestcst", 100)
|
||||||
testPWSL(t, "Ltime@go-inag~faaa!", 135)
|
testPWSL(t, "Ltime@go-inag~faaa!", 140)
|
||||||
testPWSL(t, "uTVM,TPw55:utvm,tpwstillsecure", 210)
|
testPWSL(t, "uTVM,TPw55:utvm,tpwstillsecure", 216)
|
||||||
|
|
||||||
// generated, strong
|
// generated, strong
|
||||||
testPWSL(t, "YebGPQuuoxQwyeJMvEWACTLexUUxVBFdHYqqUybBUNfBttCvWQxDdDCdYfgMPCQp", 378)
|
testPWSL(t, "YebGPQuuoxQwyeJMvEWACTLexUUxVBFdHYqqUybBUNfBttCvWQxDdDCdYfgMPCQp", 383)
|
||||||
testPWSL(t, "dpPyXmXpbECn6LWuQDJaitTTJguGfRTqNUxWfoHnBKDHvRhjR2WiQ7iDcuRJNnEd", 394)
|
testPWSL(t, "dpPyXmXpbECn6LWuQDJaitTTJguGfRTqNUxWfoHnBKDHvRhjR2WiQ7iDcuRJNnEd", 400)
|
||||||
testPWSL(t, "WgEKCp8c8{bPrG{Zo(Ms97pKt3EsR9ycz4R=kMjPp^Uafqxsd2ZTFtkfvnoueKJz", 428)
|
testPWSL(t, "WgEKCp8c8{bPrG{Zo(Ms97pKt3EsR9ycz4R=kMjPp^Uafqxsd2ZTFtkfvnoueKJz", 434)
|
||||||
testPWSL(t, "galena-fighter-festival", 127)
|
testPWSL(t, "galena-fighter-festival", 132)
|
||||||
testPWSL(t, "impotent-drug-dropout-damage", 152)
|
testPWSL(t, "impotent-drug-dropout-damage", 157)
|
||||||
testPWSL(t, "artless-newswire-rill-belgium-marplot", 196)
|
testPWSL(t, "artless-newswire-rill-belgium-marplot", 202)
|
||||||
testPWSL(t, "forbade-momenta-spook-sure-devilish-wobbly", 221)
|
testPWSL(t, "forbade-momenta-spook-sure-devilish-wobbly", 227)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testPWSL(t *testing.T, password string, expectedSecurityLevel int) {
|
func testPWSL(t *testing.T, password string, expectedSecurityLevel int) {
|
||||||
securityLevel := CalculatePasswordSecurityLevel(password, 20000)
|
securityLevel := CalculatePasswordSecurityLevel(password, 1<<20)
|
||||||
|
|
||||||
if securityLevel < expectedSecurityLevel {
|
if securityLevel < expectedSecurityLevel {
|
||||||
t.Errorf("password %s (%di): %d - expected at least %d", password, 20000, securityLevel, expectedSecurityLevel)
|
t.Errorf("password %s (%di): %d - expected at least %d", password, 1<<20, securityLevel, expectedSecurityLevel)
|
||||||
} else {
|
} else {
|
||||||
t.Logf("password %s (%di): %d", password, 20000, securityLevel)
|
t.Logf("password %s (%di): %d", password, 1<<20, securityLevel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,7 @@ func (requirements *Requirements) CheckComplianceTo(requirement *Requirements) e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if missing != nil {
|
if missing != nil {
|
||||||
return fmt.Errorf("missing tools with security requirements: %s", missing.String())
|
return fmt.Errorf("missing security requirements: %s", missing.String())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
124
session.go
124
session.go
|
@ -57,15 +57,34 @@ func (sh *managedHasher) Sum() ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSession(e *Envelope) (*Session, error) { //nolint:gocognit,gocyclo
|
func newSession(e *Envelope) (*Session, error) { //nolint:gocognit,gocyclo
|
||||||
if len(e.Tools) == 0 {
|
if e.suite == nil {
|
||||||
return nil, errors.New("envelope is missing tools")
|
return nil, errors.New("suite not loaded")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create session
|
||||||
s := &Session{
|
s := &Session{
|
||||||
envelope: e,
|
envelope: e,
|
||||||
toolRequirements: newEmptyRequirements(),
|
toolRequirements: newEmptyRequirements(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check envelope security level
|
||||||
|
if e.SecurityLevel > 0 {
|
||||||
|
err := s.checkSecurityLevel(e.SecurityLevel, func() string {
|
||||||
|
return fmt.Sprintf(`envelope "%s"`, e.Name)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// check suite security level
|
||||||
|
err := s.checkSecurityLevel(e.suite.SecurityLevel, func() string {
|
||||||
|
return fmt.Sprintf(`suite "%s"`, e.suite.ID)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepare variables
|
||||||
var (
|
var (
|
||||||
keySourceAvailable bool = false
|
keySourceAvailable bool = false
|
||||||
totalSignetsSeen int
|
totalSignetsSeen int
|
||||||
|
@ -74,13 +93,13 @@ func newSession(e *Envelope) (*Session, error) { //nolint:gocognit,gocyclo
|
||||||
)
|
)
|
||||||
|
|
||||||
// tool init loop: start
|
// tool init loop: start
|
||||||
for i, toolID := range s.envelope.Tools {
|
for i, toolID := range s.envelope.suite.Tools {
|
||||||
|
|
||||||
///////////////////////////////////////
|
///////////////////////////////////////
|
||||||
// tool init loop: check for duplicates
|
// tool init loop: check for duplicates
|
||||||
///////////////////////////////////////
|
///////////////////////////////////////
|
||||||
|
|
||||||
for j, dupeToolID := range s.envelope.Tools {
|
for j, dupeToolID := range s.envelope.suite.Tools {
|
||||||
if i != j && toolID == dupeToolID {
|
if i != j && toolID == dupeToolID {
|
||||||
return nil, fmt.Errorf("cannot use tool %s twice, each tool may be only specified once", toolID)
|
return nil, fmt.Errorf("cannot use tool %s twice, each tool may be only specified once", toolID)
|
||||||
}
|
}
|
||||||
|
@ -311,7 +330,10 @@ func newSession(e *Envelope) (*Session, error) { //nolint:gocognit,gocyclo
|
||||||
}
|
}
|
||||||
|
|
||||||
// key signets
|
// key signets
|
||||||
err := e.LoopSecrets(SignetSchemeKey, func(signet *Signet) error {
|
err = e.LoopSecrets(SignetSchemeKey, func(signet *Signet) error {
|
||||||
|
s.toolRequirements.Add(SenderAuthentication)
|
||||||
|
s.toolRequirements.Add(RecipientAuthentication)
|
||||||
|
|
||||||
totalSignetsSeen++
|
totalSignetsSeen++
|
||||||
keySourceAvailable = true
|
keySourceAvailable = true
|
||||||
return s.calcAndCheckSecurityLevel(nil, signet)
|
return s.calcAndCheckSecurityLevel(nil, signet)
|
||||||
|
@ -350,7 +372,7 @@ func newSession(e *Envelope) (*Session, error) { //nolint:gocognit,gocyclo
|
||||||
if s.toolRequirements.Empty() {
|
if s.toolRequirements.Empty() {
|
||||||
return nil, errors.New("envelope excludes all security requirements, no meaningful operation possible")
|
return nil, errors.New("envelope excludes all security requirements, no meaningful operation possible")
|
||||||
}
|
}
|
||||||
err = s.toolRequirements.CheckComplianceTo(s.envelope.requirements)
|
err = s.toolRequirements.CheckComplianceTo(s.envelope.suite.Provides)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -368,7 +390,7 @@ func newSession(e *Envelope) (*Session, error) { //nolint:gocognit,gocyclo
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if we are missing a kdf, but need one
|
// check if we are missing a kdf, but need one
|
||||||
if s.kdf == nil && len(s.signers) != len(s.envelope.Tools) {
|
if s.kdf == nil && len(s.signers) != len(s.envelope.suite.Tools) {
|
||||||
return nil, errors.New("missing a key derivation tool")
|
return nil, errors.New("missing a key derivation tool")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,6 +415,15 @@ func newSession(e *Envelope) (*Session, error) { //nolint:gocognit,gocyclo
|
||||||
return nil, fmt.Errorf("detected signet or recipient in envelope that is not used by any tool")
|
return nil, fmt.Errorf("detected signet or recipient in envelope that is not used by any tool")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check session security level
|
||||||
|
// while this should never result in an error (because every part was already checked separately) this is used as a precaution to catch errors in future code changes
|
||||||
|
err = s.checkSecurityLevel(s.SecurityLevel, func() string {
|
||||||
|
return "current session"
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -415,6 +446,9 @@ func (s *Session) calcAndCheckSecurityLevel(logic tools.ToolLogic, signet *Signe
|
||||||
// existence check is done when opening/closing
|
// existence check is done when opening/closing
|
||||||
if len(signet.Key) > 0 {
|
if len(signet.Key) > 0 {
|
||||||
switch logic.Info().Name {
|
switch logic.Info().Name {
|
||||||
|
case "SCRYPT-20":
|
||||||
|
// TODO: integrate this into the tool interface
|
||||||
|
calculatedSecurityLevel = CalculatePasswordSecurityLevel(string(signet.Key), 1<<20)
|
||||||
case "PBKDF2-SHA2-256":
|
case "PBKDF2-SHA2-256":
|
||||||
// TODO: integrate this into the tool interface
|
// TODO: integrate this into the tool interface
|
||||||
calculatedSecurityLevel = CalculatePasswordSecurityLevel(string(signet.Key), 20000)
|
calculatedSecurityLevel = CalculatePasswordSecurityLevel(string(signet.Key), 20000)
|
||||||
|
@ -451,38 +485,18 @@ func (s *Session) calcAndCheckSecurityLevel(logic tools.ToolLogic, signet *Signe
|
||||||
}
|
}
|
||||||
|
|
||||||
if signet != nil {
|
if signet != nil {
|
||||||
|
|
||||||
// signet based security level checks
|
// signet based security level checks
|
||||||
switch {
|
err = s.checkSecurityLevel(calculatedSecurityLevel, func() string {
|
||||||
case minimumSecurityLevel > 0:
|
return fmt.Sprintf(`supplied %s signet "%s"`, signet.Scheme, signet.ID)
|
||||||
// check against minimumSecurityLevel
|
})
|
||||||
// minimumSecurityLevel overrides other checks
|
|
||||||
if calculatedSecurityLevel < minimumSecurityLevel {
|
|
||||||
return fmt.Errorf(`supplied %s signet "%s" with a security level of %d is weaker than the desired security level of %d`, signet.Scheme, signet.ID, calculatedSecurityLevel, minimumSecurityLevel)
|
|
||||||
}
|
|
||||||
case s.envelope.MinimumSecurityLevel > 0 && calculatedSecurityLevel < s.envelope.MinimumSecurityLevel:
|
|
||||||
// check against envelope's minimum security level
|
|
||||||
return fmt.Errorf(`supplied %s signet "%s" with a security level of %d is weaker than the envelope's minimum security level of %d`, signet.Scheme, signet.ID, calculatedSecurityLevel, s.envelope.MinimumSecurityLevel)
|
|
||||||
case s.SecurityLevel > 0 && calculatedSecurityLevel < s.SecurityLevel:
|
|
||||||
// check against toolset's security level
|
|
||||||
return fmt.Errorf(`supplied %s signet "%s" with a security level of %d is weaker than the toolset's security level of %d`, signet.Scheme, signet.ID, calculatedSecurityLevel, s.SecurityLevel)
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
// tool based securty level checks
|
||||||
// tool based security level checks
|
err = s.checkSecurityLevel(calculatedSecurityLevel, func() string {
|
||||||
switch {
|
return "tool %s" + logic.Info().Name
|
||||||
case minimumSecurityLevel > 0:
|
})
|
||||||
// check against minimumSecurityLevel
|
}
|
||||||
// minimumSecurityLevel overrides other checks
|
if err != nil {
|
||||||
if calculatedSecurityLevel < minimumSecurityLevel {
|
return err
|
||||||
return fmt.Errorf(`tool %s with a security level of %d is weaker than the desired security level of %d`, logic.Info().Name, calculatedSecurityLevel, minimumSecurityLevel)
|
|
||||||
}
|
|
||||||
case s.envelope.MinimumSecurityLevel > 0 && calculatedSecurityLevel < s.envelope.MinimumSecurityLevel:
|
|
||||||
// check against envelope's minimum security level
|
|
||||||
return fmt.Errorf(`tool %s with a security level of %d is weaker than the envelope's minimum security level of %d`, logic.Info().Name, calculatedSecurityLevel, s.envelope.MinimumSecurityLevel)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// adapt security level of session
|
// adapt security level of session
|
||||||
|
@ -499,6 +513,42 @@ func (s *Session) calcAndCheckSecurityLevel(logic tools.ToolLogic, signet *Signe
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Session) checkSecurityLevel(levelToCheck int, subject func() string) error {
|
||||||
|
switch {
|
||||||
|
case minimumSecurityLevel > 0:
|
||||||
|
// check against minimumSecurityLevel
|
||||||
|
// minimumSecurityLevel overrides other checks
|
||||||
|
if levelToCheck < minimumSecurityLevel {
|
||||||
|
return fmt.Errorf(
|
||||||
|
`%s with a security level of %d is weaker than the desired security level of %d`,
|
||||||
|
subject(),
|
||||||
|
levelToCheck,
|
||||||
|
minimumSecurityLevel,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case s.envelope.SecurityLevel > 0:
|
||||||
|
// check against envelope's minimum security level
|
||||||
|
if levelToCheck < s.envelope.SecurityLevel {
|
||||||
|
return fmt.Errorf(
|
||||||
|
`%s with a security level of %d is weaker than the envelope's minimum security level of %d`,
|
||||||
|
subject(),
|
||||||
|
levelToCheck,
|
||||||
|
s.envelope.SecurityLevel,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case levelToCheck < defaultSecurityLevel:
|
||||||
|
// check against default security level as fallback
|
||||||
|
return fmt.Errorf(
|
||||||
|
`%s with a security level of %d is weaker than the default minimum security level of %d`,
|
||||||
|
subject(),
|
||||||
|
levelToCheck,
|
||||||
|
defaultSecurityLevel,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// NonceSize returns the nonce size to use for new letters.
|
// NonceSize returns the nonce size to use for new letters.
|
||||||
func (s *Session) NonceSize() int {
|
func (s *Session) NonceSize() int {
|
||||||
size := s.maxSecurityLevel / 32
|
size := s.maxSecurityLevel / 32
|
||||||
|
|
|
@ -220,8 +220,8 @@ func (signet *Signet) StoreKey() error {
|
||||||
|
|
||||||
// Verify verifies the signature of the signet.
|
// Verify verifies the signature of the signet.
|
||||||
func (signet *Signet) Verify() error {
|
func (signet *Signet) Verify() error {
|
||||||
// FIXME
|
// TODO
|
||||||
return errors.New("NIY")
|
return errors.New("signet verification not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Burn destroys all the key material and renders the Signet unusable. This is currently ineffective, see known issues in the project's README.
|
// Burn destroys all the key material and renders the Signet unusable. This is currently ineffective, see known issues in the project's README.
|
||||||
|
|
17
suite.go
Normal file
17
suite.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package jess
|
||||||
|
|
||||||
|
// Suite status options
|
||||||
|
const (
|
||||||
|
SuiteStatusDeprecated uint8 = 0
|
||||||
|
SuiteStatusPermitted uint8 = 1
|
||||||
|
SuiteStatusRecommended uint8 = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// Suite describes a cipher suite - a set of algorithms and the attributes they provide.
|
||||||
|
type Suite struct {
|
||||||
|
ID string
|
||||||
|
Tools []string
|
||||||
|
Provides *Requirements
|
||||||
|
SecurityLevel int
|
||||||
|
Status uint8
|
||||||
|
}
|
100
suites.go
Normal file
100
suites.go
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
package jess
|
||||||
|
|
||||||
|
var (
|
||||||
|
// 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(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 a cipher suite for encryption with a key.
|
||||||
|
SuiteKey = SuiteKeyV1
|
||||||
|
// SuitePassword is a a cipher suite for encryption with a password.
|
||||||
|
SuitePassword = SuitePasswordV1
|
||||||
|
// SuiteRcptOnly is a a cipher suite for encrypting for someone, but without verifying the sender/source.
|
||||||
|
SuiteRcptOnly = SuiteRcptOnlyV1
|
||||||
|
// SuiteSign is a a cipher suite for signing (no encryption).
|
||||||
|
SuiteSign = SuiteSignV1
|
||||||
|
// SuiteComplete is a a cipher suite for both encrypting for someone and signing.
|
||||||
|
SuiteComplete = SuiteCompleteV1
|
||||||
|
// SuiteWire is a a cipher suite for network communication, including authentication of the server, but not the client.
|
||||||
|
SuiteWire = SuiteWireV1
|
||||||
|
)
|
||||||
|
|
||||||
|
func registerSuite(suite *Suite) (suiteID string) {
|
||||||
|
// add if not exists
|
||||||
|
_, ok := suitesMap[suite.ID]
|
||||||
|
if !ok {
|
||||||
|
suitesMap[suite.ID] = suite
|
||||||
|
suitesList = append(suitesList, suite)
|
||||||
|
}
|
||||||
|
|
||||||
|
return suite.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSuite returns the suite with the given ID.
|
||||||
|
func GetSuite(suiteID string) (suite *Suite, ok bool) {
|
||||||
|
suite, ok = suitesMap[suiteID]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Suites returns all registered suites as a slice.
|
||||||
|
func Suites() []*Suite {
|
||||||
|
return suitesList
|
||||||
|
}
|
||||||
|
|
||||||
|
// SuitesMap returns all registered suites as a map.
|
||||||
|
func SuitesMap() map[string]*Suite {
|
||||||
|
return suitesMap
|
||||||
|
}
|
474
suites_test.go
Normal file
474
suites_test.go
Normal file
|
@ -0,0 +1,474 @@
|
||||||
|
package jess
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/safing/jess/hashtools"
|
||||||
|
"github.com/safing/jess/tools"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getSuite(t *testing.T, suiteID string) (suite *Suite) {
|
||||||
|
suite, ok := GetSuite(suiteID)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("suite %s does not exist", suiteID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return suite
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSuites(t *testing.T) {
|
||||||
|
for _, suite := range Suites() {
|
||||||
|
|
||||||
|
err := suiteBullshitCheck(suite)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("suite %s has incorrect property: %s", suite.ID, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
envelope, err := setupEnvelopeAndTrustStore(t, suite)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to setup test envelope for suite %s: %s", suite.ID, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if envelope == nil {
|
||||||
|
t.Errorf("suite %s has an invalid toolset", suite.ID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
session, err := envelope.Correspondence(testTrustStore)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to init session for suite %s: %s", suite.ID, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
letter, err := session.Close([]byte(testData1))
|
||||||
|
if err != nil {
|
||||||
|
tErrorf(t, "suite %s failed to close (1): %s", suite.ID, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := letter.ToJSON()
|
||||||
|
if err != nil {
|
||||||
|
tErrorf(t, "suite %s failed to json encode (1): %s", suite.ID, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// test 2: open
|
||||||
|
|
||||||
|
letter2, err := LetterFromJSON(msg)
|
||||||
|
if err != nil {
|
||||||
|
tErrorf(t, "suite %s failed to json decode (2): %s", suite.ID, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
origData2, err := letter2.Open(envelope.suite.Provides, testTrustStore)
|
||||||
|
if err != nil {
|
||||||
|
tErrorf(t, "suite %s failed to open (2): %s", suite.ID, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if string(origData2) != testData1 {
|
||||||
|
tErrorf(t, "%v original data mismatch (2): %s", suite.ID, string(origData2))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// test 2.1: verify
|
||||||
|
|
||||||
|
letter21, err := LetterFromJSON(msg)
|
||||||
|
if err != nil {
|
||||||
|
tErrorf(t, "suite %s failed to json decode (2): %s", suite.ID, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(letter21.Signatures) > 0 {
|
||||||
|
err = letter21.Verify(envelope.suite.Provides, testTrustStore)
|
||||||
|
if err != nil {
|
||||||
|
tErrorf(t, "suite %s failed to verify (2): %s", suite.ID, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func suiteBullshitCheck(suite *Suite) error { //nolint:gocognit,gocyclo
|
||||||
|
// pre checks
|
||||||
|
if suite.Provides == nil {
|
||||||
|
return errors.New("provides no requirement attributes")
|
||||||
|
}
|
||||||
|
if suite.SecurityLevel == 0 {
|
||||||
|
return errors.New("does not specify security level")
|
||||||
|
}
|
||||||
|
|
||||||
|
// create session struct for holding information
|
||||||
|
s := &Session{
|
||||||
|
envelope: &Envelope{
|
||||||
|
suite: suite,
|
||||||
|
},
|
||||||
|
toolRequirements: newEmptyRequirements(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if we are assuming we have a key
|
||||||
|
assumeKey := strings.Contains(suite.ID, "key")
|
||||||
|
if assumeKey {
|
||||||
|
s.toolRequirements.Add(SenderAuthentication)
|
||||||
|
s.toolRequirements.Add(RecipientAuthentication)
|
||||||
|
}
|
||||||
|
|
||||||
|
// tool check loop: start
|
||||||
|
for i, toolID := range suite.Tools {
|
||||||
|
|
||||||
|
////////////////////////////////////////
|
||||||
|
// tool check loop: check for duplicates
|
||||||
|
////////////////////////////////////////
|
||||||
|
|
||||||
|
for j, dupeToolID := range suite.Tools {
|
||||||
|
if i != j && toolID == dupeToolID {
|
||||||
|
return fmt.Errorf("cannot use tool %s twice, each tool may be only specified once", toolID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////
|
||||||
|
// tool check loop: parse, prep and get
|
||||||
|
///////////////////////////////////////
|
||||||
|
|
||||||
|
var (
|
||||||
|
hashTool *hashtools.HashTool
|
||||||
|
hashSumFn func() ([]byte, error)
|
||||||
|
)
|
||||||
|
|
||||||
|
// parse ID for args
|
||||||
|
var arg string
|
||||||
|
if strings.Contains(toolID, "(") {
|
||||||
|
splitted := strings.Split(toolID, "(")
|
||||||
|
toolID = splitted[0]
|
||||||
|
arg = strings.Trim(splitted[1], "()")
|
||||||
|
}
|
||||||
|
|
||||||
|
// get tool
|
||||||
|
tool, err := tools.Get(toolID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("the specified tool %s could not be found", toolID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create logic instance and add to logic and state lists
|
||||||
|
logic := tool.Factory()
|
||||||
|
s.all = append(s.all, logic)
|
||||||
|
if tool.Info.HasOption(tools.OptionHasState) {
|
||||||
|
s.toolsWithState = append(s.toolsWithState, logic)
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////
|
||||||
|
// tool check loop: assign tools to queues and add requirements
|
||||||
|
///////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
switch tool.Info.Purpose {
|
||||||
|
case tools.PurposeKeyDerivation:
|
||||||
|
if s.kdf != nil {
|
||||||
|
return fmt.Errorf("cannot use %s, you may only specify one key derivation tool and %s was already specified", tool.Info.Name, s.kdf.Info().Name)
|
||||||
|
}
|
||||||
|
s.kdf = logic
|
||||||
|
|
||||||
|
case tools.PurposePassDerivation:
|
||||||
|
if s.passDerivator != nil {
|
||||||
|
return fmt.Errorf("cannot use %s, you may only specify one password derivation tool and %s was already specified", tool.Info.Name, s.passDerivator.Info().Name)
|
||||||
|
}
|
||||||
|
s.passDerivator = logic
|
||||||
|
s.toolRequirements.Add(SenderAuthentication)
|
||||||
|
s.toolRequirements.Add(RecipientAuthentication)
|
||||||
|
|
||||||
|
case tools.PurposeKeyExchange:
|
||||||
|
s.keyExchangers = append(s.keyExchangers, logic)
|
||||||
|
s.toolRequirements.Add(RecipientAuthentication)
|
||||||
|
|
||||||
|
case tools.PurposeKeyEncapsulation:
|
||||||
|
s.keyEncapsulators = append(s.keyEncapsulators, logic)
|
||||||
|
s.toolRequirements.Add(RecipientAuthentication)
|
||||||
|
|
||||||
|
case tools.PurposeSigning:
|
||||||
|
s.signers = append(s.signers, logic)
|
||||||
|
s.toolRequirements.Add(SenderAuthentication)
|
||||||
|
|
||||||
|
case tools.PurposeIntegratedCipher:
|
||||||
|
s.integratedCiphers = append(s.integratedCiphers, logic)
|
||||||
|
s.toolRequirements.Add(Confidentiality)
|
||||||
|
s.toolRequirements.Add(Integrity)
|
||||||
|
|
||||||
|
case tools.PurposeCipher:
|
||||||
|
s.ciphers = append(s.ciphers, logic)
|
||||||
|
s.toolRequirements.Add(Confidentiality)
|
||||||
|
|
||||||
|
case tools.PurposeMAC:
|
||||||
|
s.macs = append(s.macs, logic)
|
||||||
|
s.toolRequirements.Add(Integrity)
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////
|
||||||
|
// tool check loop: process options, get hashers
|
||||||
|
////////////////////////////////////////////////
|
||||||
|
|
||||||
|
for _, option := range tool.Info.Options {
|
||||||
|
switch option {
|
||||||
|
|
||||||
|
case tools.OptionNeedsManagedHasher:
|
||||||
|
// get managed hasher list
|
||||||
|
var managedHashers map[string]*managedHasher
|
||||||
|
switch tool.Info.Purpose {
|
||||||
|
case tools.PurposeMAC:
|
||||||
|
if s.managedMACHashers == nil {
|
||||||
|
s.managedMACHashers = make(map[string]*managedHasher)
|
||||||
|
}
|
||||||
|
managedHashers = s.managedMACHashers
|
||||||
|
case tools.PurposeSigning:
|
||||||
|
if s.managedSigningHashers == nil {
|
||||||
|
s.managedSigningHashers = make(map[string]*managedHasher)
|
||||||
|
}
|
||||||
|
managedHashers = s.managedSigningHashers
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("only MAC and Signing tools may use managed hashers")
|
||||||
|
}
|
||||||
|
|
||||||
|
// get or assign a new managed hasher
|
||||||
|
mngdHasher, ok := managedHashers[arg]
|
||||||
|
if !ok {
|
||||||
|
// get hashtool
|
||||||
|
ht, err := hashtools.Get(arg)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("the specified hashtool for %s(%s) could not be found", toolID, arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// save to managed hashers
|
||||||
|
mngdHasher = &managedHasher{
|
||||||
|
tool: ht,
|
||||||
|
hash: ht.New(),
|
||||||
|
}
|
||||||
|
managedHashers[arg] = mngdHasher
|
||||||
|
}
|
||||||
|
|
||||||
|
hashTool = mngdHasher.tool
|
||||||
|
hashSumFn = mngdHasher.Sum
|
||||||
|
|
||||||
|
case tools.OptionNeedsDedicatedHasher:
|
||||||
|
hashTool, err = hashtools.Get(arg)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("the specified hashtool for %s(%s) could not be found", toolID, arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////
|
||||||
|
// tool check loop: initialize tool
|
||||||
|
///////////////////////////////////
|
||||||
|
|
||||||
|
// init tool
|
||||||
|
logic.Init(
|
||||||
|
tool,
|
||||||
|
&Helper{
|
||||||
|
session: s,
|
||||||
|
info: tool.Info,
|
||||||
|
},
|
||||||
|
hashTool,
|
||||||
|
hashSumFn,
|
||||||
|
)
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////
|
||||||
|
// tool check loop: calc and check security levels
|
||||||
|
//////////////////////////////////////////////////
|
||||||
|
|
||||||
|
err = s.calcAndCheckSecurityLevel(logic, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
} // tool check loop: end
|
||||||
|
|
||||||
|
///////////////
|
||||||
|
// final checks
|
||||||
|
///////////////
|
||||||
|
|
||||||
|
// check requirements requirements
|
||||||
|
if s.toolRequirements.Empty() {
|
||||||
|
return errors.New("suite does not provide any security attributes")
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if we have recipient auth without confidentiality
|
||||||
|
if s.toolRequirements.Has(RecipientAuthentication) &&
|
||||||
|
!s.toolRequirements.Has(Confidentiality) {
|
||||||
|
return errors.New("having recipient authentication without confidentiality does not make sense")
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if we have confidentiality without integrity
|
||||||
|
if s.toolRequirements.Has(Confidentiality) &&
|
||||||
|
!s.toolRequirements.Has(Integrity) {
|
||||||
|
return errors.New("having confidentiality without integrity does not make sense")
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if we are missing a kdf, but need one
|
||||||
|
if s.kdf == nil && len(s.signers) != len(s.envelope.suite.Tools) {
|
||||||
|
return errors.New("missing a key derivation tool")
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if have a kdf, even if we don't need one
|
||||||
|
if len(s.integratedCiphers) == 0 &&
|
||||||
|
len(s.ciphers) == 0 &&
|
||||||
|
len(s.macs) == 0 &&
|
||||||
|
s.kdf != nil {
|
||||||
|
return errors.New("key derivation tool specified, but not needed")
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////
|
||||||
|
// check if values match suite definition
|
||||||
|
/////////////////////////////////////////
|
||||||
|
|
||||||
|
// check if security level matches
|
||||||
|
if s.SecurityLevel != suite.SecurityLevel {
|
||||||
|
return fmt.Errorf("suite has incorrect security level: %d (expected %d)", suite.SecurityLevel, s.SecurityLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if requirements match
|
||||||
|
if s.toolRequirements.SerializeToNoSpec() != suite.Provides.SerializeToNoSpec() {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"suite has incorrect attributes: no %s (expected no %s)",
|
||||||
|
suite.Provides.SerializeToNoSpec(),
|
||||||
|
s.toolRequirements.SerializeToNoSpec(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////
|
||||||
|
// check if computeSuiteAttributes returns the same results
|
||||||
|
///////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
computedSuite := computeSuiteAttributes(suite.Tools, assumeKey)
|
||||||
|
if computedSuite == nil {
|
||||||
|
return errors.New("internal error: could not compute suite attributes")
|
||||||
|
}
|
||||||
|
if suite.SecurityLevel != computedSuite.SecurityLevel {
|
||||||
|
return fmt.Errorf("internal error: computeSuiteAttributes error: security level: suite=%d computed=%d", suite.SecurityLevel, computedSuite.SecurityLevel)
|
||||||
|
}
|
||||||
|
if suite.Provides.SerializeToNoSpec() != computedSuite.Provides.SerializeToNoSpec() {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"internal error: computeSuiteAttributes error: attributes: suite=no %s compute=no %s)",
|
||||||
|
suite.Provides.SerializeToNoSpec(),
|
||||||
|
computedSuite.Provides.SerializeToNoSpec(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func computeSuiteAttributes(toolIDs []string, assumeKey bool) *Suite {
|
||||||
|
new := &Suite{
|
||||||
|
Provides: newEmptyRequirements(),
|
||||||
|
SecurityLevel: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we have a key
|
||||||
|
if assumeKey {
|
||||||
|
new.Provides.Add(SenderAuthentication)
|
||||||
|
new.Provides.Add(RecipientAuthentication)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check all security levels and collect attributes
|
||||||
|
for _, toolID := range toolIDs {
|
||||||
|
|
||||||
|
///////////////////////////////////////
|
||||||
|
// tool check loop: parse, prep and get
|
||||||
|
///////////////////////////////////////
|
||||||
|
|
||||||
|
var hashTool *hashtools.HashTool
|
||||||
|
|
||||||
|
// parse ID for args
|
||||||
|
var arg string
|
||||||
|
if strings.Contains(toolID, "(") {
|
||||||
|
splitted := strings.Split(toolID, "(")
|
||||||
|
toolID = splitted[0]
|
||||||
|
arg = strings.Trim(splitted[1], "()")
|
||||||
|
}
|
||||||
|
|
||||||
|
// get tool
|
||||||
|
tool, err := tools.Get(toolID)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// create logic instance and add to logic and state lists
|
||||||
|
logic := tool.Factory()
|
||||||
|
|
||||||
|
//////////////////////////////////////
|
||||||
|
// tool check loop: collect attributes
|
||||||
|
//////////////////////////////////////
|
||||||
|
|
||||||
|
switch tool.Info.Purpose {
|
||||||
|
case tools.PurposePassDerivation:
|
||||||
|
new.Provides.Add(SenderAuthentication)
|
||||||
|
new.Provides.Add(RecipientAuthentication)
|
||||||
|
|
||||||
|
case tools.PurposeKeyExchange:
|
||||||
|
new.Provides.Add(RecipientAuthentication)
|
||||||
|
|
||||||
|
case tools.PurposeKeyEncapsulation:
|
||||||
|
new.Provides.Add(RecipientAuthentication)
|
||||||
|
|
||||||
|
case tools.PurposeSigning:
|
||||||
|
new.Provides.Add(SenderAuthentication)
|
||||||
|
|
||||||
|
case tools.PurposeIntegratedCipher:
|
||||||
|
new.Provides.Add(Confidentiality)
|
||||||
|
new.Provides.Add(Integrity)
|
||||||
|
|
||||||
|
case tools.PurposeCipher:
|
||||||
|
new.Provides.Add(Confidentiality)
|
||||||
|
|
||||||
|
case tools.PurposeMAC:
|
||||||
|
new.Provides.Add(Integrity)
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////
|
||||||
|
// tool check loop: process options, get hashers
|
||||||
|
////////////////////////////////////////////////
|
||||||
|
|
||||||
|
for _, option := range tool.Info.Options {
|
||||||
|
switch option {
|
||||||
|
case tools.OptionNeedsManagedHasher,
|
||||||
|
tools.OptionNeedsDedicatedHasher:
|
||||||
|
hashTool, err = hashtools.Get(arg)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////
|
||||||
|
// tool check loop: initialize tool
|
||||||
|
///////////////////////////////////
|
||||||
|
|
||||||
|
// init tool
|
||||||
|
logic.Init(
|
||||||
|
tool,
|
||||||
|
&Helper{
|
||||||
|
info: tool.Info,
|
||||||
|
},
|
||||||
|
hashTool,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
|
||||||
|
//////////////////////////////////////////
|
||||||
|
// tool check loop: compute security level
|
||||||
|
//////////////////////////////////////////
|
||||||
|
|
||||||
|
toolSecurityLevel, err := logic.SecurityLevel(nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if new.SecurityLevel == 0 || toolSecurityLevel < new.SecurityLevel {
|
||||||
|
new.SecurityLevel = toolSecurityLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return new
|
||||||
|
}
|
|
@ -35,6 +35,9 @@ func (oaep *RsaOAEP) EncapsulateKey(key []byte, signet tools.SignetInt) ([]byte,
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, tools.ErrInvalidKey
|
return nil, tools.ErrInvalidKey
|
||||||
}
|
}
|
||||||
|
if rsaPubKey == nil {
|
||||||
|
return nil, tools.ErrInvalidKey
|
||||||
|
}
|
||||||
|
|
||||||
// check key length: The message must be no longer than the length of the public modulus minus twice the hash length, minus a further 2.
|
// check key length: The message must be no longer than the length of the public modulus minus twice the hash length, minus a further 2.
|
||||||
maxMsgSize := rsaPubKey.Size() - (2 * oaep.HashTool().DigestSize) - 2
|
maxMsgSize := rsaPubKey.Size() - (2 * oaep.HashTool().DigestSize) - 2
|
||||||
|
|
159
tools_test.go
159
tools_test.go
|
@ -1,7 +1,12 @@
|
||||||
package jess
|
package jess
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/safing/jess/hashtools"
|
||||||
|
|
||||||
"github.com/safing/jess/tools"
|
"github.com/safing/jess/tools"
|
||||||
|
|
||||||
|
@ -32,7 +37,39 @@ func TestConformity(t *testing.T) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPasswordHashingSpeed(t *testing.T) {
|
||||||
|
// skip in short tests and when not running comprehensive
|
||||||
|
if testing.Short() || !runComprehensiveTestsActive {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// run this test with
|
||||||
|
// go test -timeout 10m github.com/safing/jess -v -count=1 -ldflags "-X github.com/safing/jess.RunComprehensiveTests=true" -run ^TestPasswordHashingSpeed$
|
||||||
|
|
||||||
|
for _, tool := range tools.AsList() {
|
||||||
|
if tool.Info.Purpose == tools.PurposePassDerivation {
|
||||||
|
password := []byte(testPassword1)
|
||||||
|
salt, err := RandomBytes(4)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
_, err = tool.StaticLogic.DeriveKeyFromPassword(password, salt)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Logf("%s took %s to derive key from password", tool.Info.Name, time.Since(start))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:gocognit,gocyclo
|
||||||
func TestSignetHandling(t *testing.T) {
|
func TestSignetHandling(t *testing.T) {
|
||||||
|
hashTool, err := hashtools.Get("SHA2-256")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
for _, tool := range tools.AsList() {
|
for _, tool := range tools.AsList() {
|
||||||
switch tool.Info.Purpose {
|
switch tool.Info.Purpose {
|
||||||
case tools.PurposeKeyExchange,
|
case tools.PurposeKeyExchange,
|
||||||
|
@ -88,6 +125,128 @@ func TestSignetHandling(t *testing.T) {
|
||||||
t.Fatalf("failed to load %s recipient: %s", tool.Info.Name, err)
|
t.Fatalf("failed to load %s recipient: %s", tool.Info.Name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// store signet
|
||||||
|
signetJSON, err := json.Marshal(signet)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to serialize %s signet: %s", tool.Info.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// load signet
|
||||||
|
loadedSignet := &Signet{}
|
||||||
|
err = json.Unmarshal(signetJSON, loadedSignet)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to parse serialized %s signet: %s", tool.Info.Name, err)
|
||||||
|
}
|
||||||
|
err = loadedSignet.LoadKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to load key of %s signet: %s", tool.Info.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// store rcpt
|
||||||
|
rcptJSON, err := json.Marshal(rcpt)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to serialize %s rcpt: %s", tool.Info.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// load rcpt
|
||||||
|
loadedRcpt := &Signet{}
|
||||||
|
err = json.Unmarshal(rcptJSON, loadedRcpt)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to parse serialized %s rcpt: %s", tool.Info.Name, err)
|
||||||
|
}
|
||||||
|
err = loadedRcpt.LoadKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to load key of %s rcpt: %s", tool.Info.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// load tool
|
||||||
|
err = loadedSignet.loadTool()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to load tool of %s: %s", tool.Info.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// init tool with hashtool
|
||||||
|
toolLogic := tool.Factory()
|
||||||
|
hasher := managedHasher{
|
||||||
|
tool: hashTool,
|
||||||
|
hash: hashTool.New(),
|
||||||
|
}
|
||||||
|
toolLogic.Init(
|
||||||
|
tool,
|
||||||
|
&Helper{info: tool.Info},
|
||||||
|
hashTool,
|
||||||
|
hasher.Sum,
|
||||||
|
)
|
||||||
|
|
||||||
|
// do an operation
|
||||||
|
switch loadedSignet.tool.Info.Purpose {
|
||||||
|
case tools.PurposeKeyExchange:
|
||||||
|
// create and generate signet
|
||||||
|
peerSignet := NewSignetBase(tool)
|
||||||
|
err := peerSignet.GenerateKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to generate key with %s peer: %s", tool.Info.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// transform to recipient
|
||||||
|
peerRcpt, err := peerSignet.AsRecipient()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to get %s peer as recipient: %s", tool.Info.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sharedSecret1, err := toolLogic.MakeSharedKey(loadedSignet, peerRcpt)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to make shared secret 1 with %s: %s", tool.Info.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sharedSecret2, err := toolLogic.MakeSharedKey(peerSignet, loadedRcpt)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to make shared secret 2 with %s: %s", tool.Info.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(sharedSecret1, sharedSecret2) {
|
||||||
|
t.Fatalf("shared secrets made with %s do not match, got:\ns1: %v\ns2: %v", tool.Info.Name, sharedSecret1, sharedSecret2)
|
||||||
|
}
|
||||||
|
case tools.PurposeKeyEncapsulation:
|
||||||
|
origKey, err := RandomBytes(16)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to generate test key: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wrappedKey, err := toolLogic.EncapsulateKey(origKey, loadedRcpt)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to encapsulate key with %s: %s", tool.Info.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
unwrappedKey, err := toolLogic.UnwrapKey(wrappedKey, loadedSignet)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to unwrap key with %s: %s", tool.Info.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(origKey, unwrappedKey) {
|
||||||
|
t.Fatalf("original and unwrapped key with %s do not match, got:\norig: %v\nunwrapped: %v", tool.Info.Name, origKey, unwrappedKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
case tools.PurposeSigning:
|
||||||
|
testData, err := RandomBytes(16)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to generate test data: %s", err)
|
||||||
|
}
|
||||||
|
_, err = hasher.hash.Write(testData)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to write to hash: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
signature, err := toolLogic.Sign(testData, nil, loadedSignet)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to sign with %s: %s", tool.Info.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = toolLogic.Verify(testData, nil, signature, loadedRcpt)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to verify with %s: %s", tool.Info.Name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,9 +65,6 @@ func WriteEnvelopeToFile(envelope *jess.Envelope, filename string) error {
|
||||||
return errInvalidEnvelopeNameChars
|
return errInvalidEnvelopeNameChars
|
||||||
}
|
}
|
||||||
|
|
||||||
// serialize requirements
|
|
||||||
envelope.SerializeRequirements()
|
|
||||||
|
|
||||||
// serialize
|
// serialize
|
||||||
data, err := dsd.DumpIndent(envelope, dsd.JSON, "\t")
|
data, err := dsd.DumpIndent(envelope, dsd.JSON, "\t")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -100,8 +97,8 @@ func LoadEnvelopeFromFile(filename string) (*jess.Envelope, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse requirements from "No"
|
// load suite using SuiteID
|
||||||
err = envelope.LoadRequirements()
|
err = envelope.LoadSuite()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue