diff options
| author | jwijenbergh <jeroenwijenbergh@protonmail.com> | 2022-02-09 18:10:09 +0100 |
|---|---|---|
| committer | jwijenbergh <jeroenwijenbergh@protonmail.com> | 2022-04-05 12:26:10 +0200 |
| commit | 23e63807085b13a9b221c3374d05099559583011 (patch) | |
| tree | 61f53f9c8282ba60edba322499a3b68317bc53a7 | |
| parent | 70b4bad8904fe02fe4d783b75c6137ba959363ec (diff) | |
Add signature verification to list retrieval
- Move test data to src
- Verify signatures by calling the Verify method
- Add a customizable parameter to force prehashed signatures
Signed-off-by: jwijenbergh <jeroenwijenbergh@protonmail.com>
| -rw-r--r-- | go.mod | 2 | ||||
| -rw-r--r-- | src/server.go | 56 | ||||
| -rw-r--r-- | src/test_data/empty (renamed from test_data/empty) | 0 | ||||
| -rw-r--r-- | src/test_data/generate.sh (renamed from test_data/generate.sh) | 0 | ||||
| -rw-r--r-- | src/test_data/generate_forged.py (renamed from test_data/generate_forged.py) | 0 | ||||
| -rw-r--r-- | src/test_data/organization_list.json (renamed from test_data/organization_list.json) | 0 | ||||
| -rw-r--r-- | src/test_data/organization_list.json.minisig (renamed from test_data/organization_list.json.minisig) | 0 | ||||
| -rw-r--r-- | src/test_data/organization_list.json.tc_servlist.minisig (renamed from test_data/organization_list.json.tc_servlist.minisig) | 0 | ||||
| -rw-r--r-- | src/test_data/other_list.json (renamed from test_data/other_list.json) | 0 | ||||
| -rw-r--r-- | src/test_data/other_list.json.minisig (renamed from test_data/other_list.json.minisig) | 0 | ||||
| -rw-r--r-- | src/test_data/public.key (renamed from test_data/public.key) | 0 | ||||
| -rw-r--r-- | src/test_data/random.txt (renamed from test_data/random.txt) | 0 | ||||
| -rw-r--r-- | src/test_data/secret.key (renamed from test_data/secret.key) | 0 | ||||
| -rw-r--r-- | src/test_data/server_list.json (renamed from test_data/server_list.json) | 0 | ||||
| -rw-r--r-- | src/test_data/server_list.json.blake2b (renamed from test_data/server_list.json.blake2b) | bin | 64 -> 64 bytes | |||
| -rw-r--r-- | src/test_data/server_list.json.forged_keyid.minisig (renamed from test_data/server_list.json.forged_keyid.minisig) | 0 | ||||
| -rw-r--r-- | src/test_data/server_list.json.forged_pure.minisig (renamed from test_data/server_list.json.forged_pure.minisig) | 0 | ||||
| -rw-r--r-- | src/test_data/server_list.json.large_time.minisig (renamed from test_data/server_list.json.large_time.minisig) | 0 | ||||
| -rw-r--r-- | src/test_data/server_list.json.minisig (renamed from test_data/server_list.json.minisig) | 0 | ||||
| -rw-r--r-- | src/test_data/server_list.json.pure.minisig (renamed from test_data/server_list.json.pure.minisig) | 0 | ||||
| -rw-r--r-- | src/test_data/server_list.json.tc_earliertime.minisig (renamed from test_data/server_list.json.tc_earliertime.minisig) | 0 | ||||
| -rw-r--r-- | src/test_data/server_list.json.tc_emptyfile.minisig (renamed from test_data/server_list.json.tc_emptyfile.minisig) | 0 | ||||
| -rw-r--r-- | src/test_data/server_list.json.tc_emptytime.minisig (renamed from test_data/server_list.json.tc_emptytime.minisig) | 0 | ||||
| -rw-r--r-- | src/test_data/server_list.json.tc_latertime.minisig (renamed from test_data/server_list.json.tc_latertime.minisig) | 0 | ||||
| -rw-r--r-- | src/test_data/server_list.json.tc_nofile.minisig (renamed from test_data/server_list.json.tc_nofile.minisig) | 0 | ||||
| -rw-r--r-- | src/test_data/server_list.json.tc_nohashed.minisig (renamed from test_data/server_list.json.tc_nohashed.minisig) | 0 | ||||
| -rw-r--r-- | src/test_data/server_list.json.tc_notime.minisig (renamed from test_data/server_list.json.tc_notime.minisig) | 0 | ||||
| -rw-r--r-- | src/test_data/server_list.json.tc_orglist.minisig (renamed from test_data/server_list.json.tc_orglist.minisig) | 0 | ||||
| -rw-r--r-- | src/test_data/server_list.json.tc_otherfile.minisig (renamed from test_data/server_list.json.tc_otherfile.minisig) | 0 | ||||
| -rw-r--r-- | src/test_data/server_list.json.tc_random.minisig (renamed from test_data/server_list.json.tc_random.minisig) | 0 | ||||
| -rw-r--r-- | src/test_data/server_list.json.wrong_key.minisig (renamed from test_data/server_list.json.wrong_key.minisig) | 0 | ||||
| -rw-r--r-- | src/test_data/wrong_public.key (renamed from test_data/wrong_public.key) | 0 | ||||
| -rw-r--r-- | src/test_data/wrong_secret.key (renamed from test_data/wrong_secret.key) | 0 | ||||
| -rw-r--r-- | src/verify.go | 14 | ||||
| -rw-r--r-- | src/verify_test.go (renamed from verify_test.go) | 9 |
35 files changed, 61 insertions, 20 deletions
@@ -1,4 +1,4 @@ -module eduvpn-common +module github.com/jwijenbergh/eduvpn-common go 1.15 diff --git a/src/server.go b/src/server.go index 7973654..314ceae 100644 --- a/src/server.go +++ b/src/server.go @@ -1,4 +1,4 @@ -package eduvpn_discovery +package eduvpn import ( "encoding/json" @@ -36,41 +36,79 @@ type servers struct { } `json:"server_list"` } -// Helper function that gets a disco json -// TODO: Verify signature -func getDiscoJson(jsonFile string, structure interface{}) bool { - url := "https://disco.eduvpn.org/v2/" + jsonFile +func getFileUrl(url string) ([]byte, bool) { // Do a Get request to the specified url resp, reqErr := http.Get(url) if reqErr != nil { fmt.Println("error making request") - return false + return nil, false } + // Close the response body at the end + defer resp.Body.Close() + // Check if http response code is ok + if resp.StatusCode != http.StatusOK { + return nil, false + } // Read the body body, readErr := ioutil.ReadAll(resp.Body) if readErr != nil { fmt.Println("error reading body of request") + return nil, false + } + return body, true +} + +// Helper function that gets a disco json +// TODO: Verify signature +func getDiscoJson(jsonFile string, structure interface{}) bool { + // Get json data + fileUrl := "https://disco.eduvpn.org/v2/" + jsonFile + fileBody, fileSuccess := getFileUrl(fileUrl) + + if !fileSuccess { + fmt.Println("error getting file") + } + + // Get signature + sigUrl := fileUrl + ".minisig" + sigBody, sigSuccess := getFileUrl(sigUrl) + + if !sigSuccess { + fmt.Println("error getting signature") + return false + } + + // Verify signature + // TODO: Handle this by keeping track of the previous sign time + // Wrappers must do this? + var previousSigTime uint64 = 0 + forcePrehash := false + verifySuccess, verifyErr := Verify(string(sigBody), fileBody, jsonFile, previousSigTime, forcePrehash) + + if !verifySuccess || verifyErr != nil { + fmt.Printf("signature is invalid with error: %s\n", verifyErr) return false } // Parse the json using the predefined struct - error := json.Unmarshal([]byte(body), &structure) + error := json.Unmarshal([]byte(fileBody), &structure) if error != nil { fmt.Println("error parsing server json") return false } + return true } // Get the organization list -func getOrganizationList() bool { +func GetOrganizationList() bool { organizations := organizations{} return getDiscoJson("organization_list.json", &organizations) } // Get the server list -func getServerList() bool { +func GetServerList() bool { servers := servers{} return getDiscoJson("server_list.json", &servers) } diff --git a/test_data/empty b/src/test_data/empty index e69de29..e69de29 100644 --- a/test_data/empty +++ b/src/test_data/empty diff --git a/test_data/generate.sh b/src/test_data/generate.sh index b1b4545..b1b4545 100644 --- a/test_data/generate.sh +++ b/src/test_data/generate.sh diff --git a/test_data/generate_forged.py b/src/test_data/generate_forged.py index 843b32d..843b32d 100644 --- a/test_data/generate_forged.py +++ b/src/test_data/generate_forged.py diff --git a/test_data/organization_list.json b/src/test_data/organization_list.json index 8c53044..8c53044 100644 --- a/test_data/organization_list.json +++ b/src/test_data/organization_list.json diff --git a/test_data/organization_list.json.minisig b/src/test_data/organization_list.json.minisig index 1fa546e..1fa546e 100644 --- a/test_data/organization_list.json.minisig +++ b/src/test_data/organization_list.json.minisig diff --git a/test_data/organization_list.json.tc_servlist.minisig b/src/test_data/organization_list.json.tc_servlist.minisig index a7fe41f..a7fe41f 100644 --- a/test_data/organization_list.json.tc_servlist.minisig +++ b/src/test_data/organization_list.json.tc_servlist.minisig diff --git a/test_data/other_list.json b/src/test_data/other_list.json index 25ba1a8..25ba1a8 100644 --- a/test_data/other_list.json +++ b/src/test_data/other_list.json diff --git a/test_data/other_list.json.minisig b/src/test_data/other_list.json.minisig index eaa2248..eaa2248 100644 --- a/test_data/other_list.json.minisig +++ b/src/test_data/other_list.json.minisig diff --git a/test_data/public.key b/src/test_data/public.key index 72676d3..72676d3 100644 --- a/test_data/public.key +++ b/src/test_data/public.key diff --git a/test_data/random.txt b/src/test_data/random.txt index b6fc4c6..b6fc4c6 100644 --- a/test_data/random.txt +++ b/src/test_data/random.txt diff --git a/test_data/secret.key b/src/test_data/secret.key index 6e4af37..6e4af37 100644 --- a/test_data/secret.key +++ b/src/test_data/secret.key diff --git a/test_data/server_list.json b/src/test_data/server_list.json index 67c4c8d..67c4c8d 100644 --- a/test_data/server_list.json +++ b/src/test_data/server_list.json diff --git a/test_data/server_list.json.blake2b b/src/test_data/server_list.json.blake2b Binary files differindex 5d2ca5a..5d2ca5a 100644 --- a/test_data/server_list.json.blake2b +++ b/src/test_data/server_list.json.blake2b diff --git a/test_data/server_list.json.forged_keyid.minisig b/src/test_data/server_list.json.forged_keyid.minisig index efa349d..efa349d 100644 --- a/test_data/server_list.json.forged_keyid.minisig +++ b/src/test_data/server_list.json.forged_keyid.minisig diff --git a/test_data/server_list.json.forged_pure.minisig b/src/test_data/server_list.json.forged_pure.minisig index a362504..a362504 100644 --- a/test_data/server_list.json.forged_pure.minisig +++ b/src/test_data/server_list.json.forged_pure.minisig diff --git a/test_data/server_list.json.large_time.minisig b/src/test_data/server_list.json.large_time.minisig index 79a2a52..79a2a52 100644 --- a/test_data/server_list.json.large_time.minisig +++ b/src/test_data/server_list.json.large_time.minisig diff --git a/test_data/server_list.json.minisig b/src/test_data/server_list.json.minisig index 143585b..143585b 100644 --- a/test_data/server_list.json.minisig +++ b/src/test_data/server_list.json.minisig diff --git a/test_data/server_list.json.pure.minisig b/src/test_data/server_list.json.pure.minisig index 57dccfc..57dccfc 100644 --- a/test_data/server_list.json.pure.minisig +++ b/src/test_data/server_list.json.pure.minisig diff --git a/test_data/server_list.json.tc_earliertime.minisig b/src/test_data/server_list.json.tc_earliertime.minisig index 03da710..03da710 100644 --- a/test_data/server_list.json.tc_earliertime.minisig +++ b/src/test_data/server_list.json.tc_earliertime.minisig diff --git a/test_data/server_list.json.tc_emptyfile.minisig b/src/test_data/server_list.json.tc_emptyfile.minisig index a7aa3ed..a7aa3ed 100644 --- a/test_data/server_list.json.tc_emptyfile.minisig +++ b/src/test_data/server_list.json.tc_emptyfile.minisig diff --git a/test_data/server_list.json.tc_emptytime.minisig b/src/test_data/server_list.json.tc_emptytime.minisig index d3ef01e..d3ef01e 100644 --- a/test_data/server_list.json.tc_emptytime.minisig +++ b/src/test_data/server_list.json.tc_emptytime.minisig diff --git a/test_data/server_list.json.tc_latertime.minisig b/src/test_data/server_list.json.tc_latertime.minisig index 8237123..8237123 100644 --- a/test_data/server_list.json.tc_latertime.minisig +++ b/src/test_data/server_list.json.tc_latertime.minisig diff --git a/test_data/server_list.json.tc_nofile.minisig b/src/test_data/server_list.json.tc_nofile.minisig index 3c1dcbe..3c1dcbe 100644 --- a/test_data/server_list.json.tc_nofile.minisig +++ b/src/test_data/server_list.json.tc_nofile.minisig diff --git a/test_data/server_list.json.tc_nohashed.minisig b/src/test_data/server_list.json.tc_nohashed.minisig index 1d140c1..1d140c1 100644 --- a/test_data/server_list.json.tc_nohashed.minisig +++ b/src/test_data/server_list.json.tc_nohashed.minisig diff --git a/test_data/server_list.json.tc_notime.minisig b/src/test_data/server_list.json.tc_notime.minisig index 39625c3..39625c3 100644 --- a/test_data/server_list.json.tc_notime.minisig +++ b/src/test_data/server_list.json.tc_notime.minisig diff --git a/test_data/server_list.json.tc_orglist.minisig b/src/test_data/server_list.json.tc_orglist.minisig index 7c2a3a8..7c2a3a8 100644 --- a/test_data/server_list.json.tc_orglist.minisig +++ b/src/test_data/server_list.json.tc_orglist.minisig diff --git a/test_data/server_list.json.tc_otherfile.minisig b/src/test_data/server_list.json.tc_otherfile.minisig index 58a29b2..58a29b2 100644 --- a/test_data/server_list.json.tc_otherfile.minisig +++ b/src/test_data/server_list.json.tc_otherfile.minisig diff --git a/test_data/server_list.json.tc_random.minisig b/src/test_data/server_list.json.tc_random.minisig index 7240980..7240980 100644 --- a/test_data/server_list.json.tc_random.minisig +++ b/src/test_data/server_list.json.tc_random.minisig diff --git a/test_data/server_list.json.wrong_key.minisig b/src/test_data/server_list.json.wrong_key.minisig index 5a83c0e..5a83c0e 100644 --- a/test_data/server_list.json.wrong_key.minisig +++ b/src/test_data/server_list.json.wrong_key.minisig diff --git a/test_data/wrong_public.key b/src/test_data/wrong_public.key index aa794d4..aa794d4 100644 --- a/test_data/wrong_public.key +++ b/src/test_data/wrong_public.key diff --git a/test_data/wrong_secret.key b/src/test_data/wrong_secret.key index 68e9092..68e9092 100644 --- a/test_data/wrong_secret.key +++ b/src/test_data/wrong_secret.key diff --git a/src/verify.go b/src/verify.go index 336ba73..b7077b8 100644 --- a/src/verify.go +++ b/src/verify.go @@ -1,4 +1,4 @@ -package eduvpn_discovery +package eduvpn import ( "fmt" @@ -19,11 +19,13 @@ func getKeys() []string { // expectedFileName must be set to the file type to be verified, either "server_list.json" or "organization_list.json". // minSign must be set to the minimum UNIX timestamp (without milliseconds) for the file version. // This value should not be smaller than the time on the previous document verified. +// forcePrehash indicates whether or not we want to force the use of prehashed signatures +// In the future we want to remove this parameter and only allow prehashed signatures // // The return value will either be (true, nil) for a valid signature or (false, VerifyError) otherwise. // // Verify is a wrapper around verifyWithKeys where allowedPublicKeys is set to the list from https://git.sr.ht/~eduvpn/disco.eduvpn.org#public-keys. -func Verify(signatureFileContent string, signedJson []byte, expectedFileName string, minSignTime uint64) (bool, error) { +func Verify(signatureFileContent string, signedJson []byte, expectedFileName string, minSignTime uint64, forcePrehash bool) (bool, error) { keyStrs := getKeys() if extraKey != "" { keyStrs = append(keyStrs, extraKey) @@ -32,7 +34,7 @@ func Verify(signatureFileContent string, signedJson []byte, expectedFileName str panic(err) } } - valid, err := verifyWithKeys(signatureFileContent, signedJson, expectedFileName, minSignTime, keyStrs) + valid, err := verifyWithKeys(signatureFileContent, signedJson, expectedFileName, minSignTime, keyStrs, forcePrehash) if err != nil { if err.(detailedVerifyError).Code == errInvalidPublicKey { panic(err) // This should not happen unless keyStrs has an invalid key @@ -78,13 +80,13 @@ func (err VerifyError) Unwrap() error { // verifyWithKeys verifies the Minisign signature in signatureFileContent (minisig file format) over the server_list/organization_list JSON in signedJson. // // Verification is performed using a matching key in allowedPublicKeys. -// The signature is checked to be a Blake2b-prehashed Ed25519 Minisign signature with a valid trusted comment. +// The signature is checked to be a Ed25519 Minisign (optionally Ed25519 Blake2b-512 prehashed, see forcePrehash) signature with a valid trusted comment. // The file type that is verified is indicated by expectedFileName, which must be one of "server_list.json"/"organization_list.json". // The trusted comment is checked to be of the form "timestamp:<timestamp>\tfile:<expectedFileName>", optionally suffixed by something, e.g. "\thashed". // The signature is checked to have a timestamp with a value of at least minSignTime, which is a UNIX timestamp without milliseconds. // // The return value will either be (true, nil) on success or (false, detailedVerifyError) on failure. -func verifyWithKeys(signatureFileContent string, signedJson []byte, expectedFileName string, minSignTime uint64, allowedPublicKeys []string) (bool, error) { +func verifyWithKeys(signatureFileContent string, signedJson []byte, expectedFileName string, minSignTime uint64, allowedPublicKeys []string, forcePrehash bool) (bool, error) { switch expectedFileName { case "server_list.json", "organization_list.json": break @@ -98,7 +100,7 @@ func verifyWithKeys(signatureFileContent string, signedJson []byte, expectedFile } // Check if signature is prehashed, see https://jedisct1.github.io/minisign/#signature-format - if sig.SignatureAlgorithm != [2]byte{'E', 'D'} { + if forcePrehash && sig.SignatureAlgorithm != [2]byte{'E', 'D'} { return false, detailedVerifyError{errInvalidSignatureAlgorithm, "BLAKE2b-prehashed EdDSA signature required", nil} } diff --git a/verify_test.go b/src/verify_test.go index 769bfbc..b9cc033 100644 --- a/verify_test.go +++ b/src/verify_test.go @@ -1,4 +1,4 @@ -package eduvpn_discovery +package eduvpn import ( "bufio" @@ -99,14 +99,15 @@ func Test_verifyWithKeys(t *testing.T) { loadFile(test.jsonFile) } + forcePrehash := true for _, tt := range tests { t.Run(tt.testName, func(t *testing.T) { t.Parallel() valid, err := verifyWithKeys(string(files[tt.signatureFile]), files[tt.jsonFile], - tt.expectedFileName, tt.minSignTime, tt.allowedPks) + tt.expectedFileName, tt.minSignTime, tt.allowedPks, forcePrehash) compareResults(t, valid, err, int(tt.result), func() string { - return fmt.Sprintf("verifyWithKeys(%q, %q, %q, %v, %v)", - tt.signatureFile, tt.jsonFile, tt.expectedFileName, tt.minSignTime, tt.allowedPks) + return fmt.Sprintf("verifyWithKeys(%q, %q, %q, %v, %v, %t)", + tt.signatureFile, tt.jsonFile, tt.expectedFileName, tt.minSignTime, tt.allowedPks, forcePrehash) }) }) } |
