summaryrefslogtreecommitdiff
path: root/verify.go
diff options
context:
space:
mode:
authorjwijenbergh <jeroenwijenbergh@protonmail.com>2022-02-08 15:56:53 +0100
committerjwijenbergh <jeroenwijenbergh@protonmail.com>2022-04-05 12:26:10 +0200
commit70b4bad8904fe02fe4d783b75c6137ba959363ec (patch)
treecaa512596ed5accde73acb31b232d5540edfb401 /verify.go
parent32bd89a77c8aa2fed235291171791def04724b8d (diff)
Go: Begin working on abstracting server/organization list
Signed-off-by: jwijenbergh <jeroenwijenbergh@protonmail.com>
Diffstat (limited to 'verify.go')
-rw-r--r--verify.go200
1 files changed, 0 insertions, 200 deletions
diff --git a/verify.go b/verify.go
deleted file mode 100644
index 336ba73..0000000
--- a/verify.go
+++ /dev/null
@@ -1,200 +0,0 @@
-package eduvpn_discovery
-
-import (
- "fmt"
- "github.com/jedisct1/go-minisign"
- "os"
-)
-
-// getKeys returns keys taken from https://git.sr.ht/~eduvpn/disco.eduvpn.org#public-keys.
-func getKeys() []string {
- return []string{
- "RWRtBSX1alxyGX+Xn3LuZnWUT0w//B6EmTJvgaAxBMYzlQeI+jdrO6KF", // fkooman@tuxed.net, kolla@uninett.no
- "RWQKqtqvd0R7rUDp0rWzbtYPA3towPWcLDCl7eY9pBMMI/ohCmrS0WiM", // RoSp
- }
-}
-
-// Verify verifies the signature (.minisig file format) on signedJson.
-//
-// 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.
-//
-// 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) {
- keyStrs := getKeys()
- if extraKey != "" {
- keyStrs = append(keyStrs, extraKey)
- _, err := fmt.Fprintf(os.Stderr, "INSECURE TEST MODE ENABLED WITH KEY %q\n", extraKey)
- if err != nil {
- panic(err)
- }
- }
- valid, err := verifyWithKeys(signatureFileContent, signedJson, expectedFileName, minSignTime, keyStrs)
- if err != nil {
- if err.(detailedVerifyError).Code == errInvalidPublicKey {
- panic(err) // This should not happen unless keyStrs has an invalid key
- }
- return valid, err.(detailedVerifyError).ToVerifyError()
- }
- return valid, nil
-}
-
-// extraKey is an extra allowed key for testing.
-var extraKey = ""
-
-// InsecureTestingSetExtraKey adds an extra allowed key for verification with Verify.
-// ONLY USE FOR TESTING. Applies to all threads. Probably not thread-safe. Do not call in parallel to Verify.
-//
-// keyString must be a Base64-encoded Minisign key, or empty to reset.
-func InsecureTestingSetExtraKey(keyString string) {
- extraKey = keyString
-}
-
-// VerifyErrorCode Simplified error code for public interface.
-type VerifyErrorCode int8
-
-const (
- ErrUnknownExpectedFileName VerifyErrorCode = iota + 1 // Unknown expected file name specified. The signature has not been verified.
- ErrInvalidSignature // Signature is invalid (for the expected file type).
- ErrInvalidSignatureUnknownKey // Signature was created with an unknown key and has not been verified.
- ErrTooOld // Signature timestamp smaller than specified minimum signing time (rollback).
-)
-
-type VerifyError struct {
- Code VerifyErrorCode
- Detailed detailedVerifyError
-}
-
-func (err VerifyError) Error() string {
- return err.Detailed.Error()
-}
-func (err VerifyError) Unwrap() error {
- return err.Detailed
-}
-
-// 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 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) {
- switch expectedFileName {
- case "server_list.json", "organization_list.json":
- break
- default:
- return false, detailedVerifyError{errUnknownExpectedFileName, "invalid expected file name", nil}
- }
-
- sig, err := minisign.DecodeSignature(signatureFileContent)
- if err != nil {
- return false, detailedVerifyError{errInvalidSignatureFormat, "invalid signature format", err}
- }
-
- // Check if signature is prehashed, see https://jedisct1.github.io/minisign/#signature-format
- if sig.SignatureAlgorithm != [2]byte{'E', 'D'} {
- return false, detailedVerifyError{errInvalidSignatureAlgorithm, "BLAKE2b-prehashed EdDSA signature required", nil}
- }
-
- // Find allowed key used for signature
- for _, keyStr := range allowedPublicKeys {
- key, err := minisign.NewPublicKey(keyStr)
- if err != nil {
- // Should only happen if Verify is wrong or extraKey is invalid
- return false, detailedVerifyError{errInvalidPublicKey, "internal error: could not create public key", err}
- }
-
- if sig.KeyId != key.KeyId {
- continue // Wrong key
- }
-
- valid, err := key.Verify(signedJson, sig)
- if !valid {
- return false, detailedVerifyError{errInvalidSignature, "invalid signature", err}
- }
-
- // Parse trusted comment
- var signTime uint64
- var sigFileName string
- // sigFileName cannot have spaces
- _, err = fmt.Sscanf(sig.TrustedComment, "trusted comment: timestamp:%d\tfile:%s", &signTime, &sigFileName)
- if err != nil {
- return false, detailedVerifyError{errInvalidTrustedComment, "failed to interpret trusted comment", err}
- }
-
- if sigFileName != expectedFileName {
- return false, detailedVerifyError{errWrongFileName, "signature was created for wrong file", nil}
- }
-
- if signTime < minSignTime {
- return false, detailedVerifyError{errTooOld, "signature was created a time earlier than the minimum time specified", nil}
- }
-
- return true, nil
- }
-
- // No matching allowed key found
- return false, detailedVerifyError{errWrongKey, "signature was created with an unknown key", nil}
-}
-
-// detailedVerifyErrorCode used for unit tests.
-type detailedVerifyErrorCode int8
-
-const (
- errUnknownExpectedFileName detailedVerifyErrorCode = iota + 1
- errInvalidSignatureFormat
- errInvalidSignatureAlgorithm
- errInvalidPublicKey
- errInvalidSignature
- errInvalidTrustedComment
- errWrongFileName
- errTooOld
- errWrongKey
-)
-
-func (code detailedVerifyErrorCode) ToVerifyErrorCode() VerifyErrorCode {
- switch code {
- case errUnknownExpectedFileName:
- return ErrUnknownExpectedFileName
- case errInvalidSignatureFormat:
- return ErrInvalidSignature
- case errInvalidSignatureAlgorithm:
- return ErrInvalidSignature
- case errInvalidPublicKey:
- panic("errInvalidPublicKey cannot be converted to VerifyErrorCode")
- case errInvalidSignature:
- return ErrInvalidSignature
- case errInvalidTrustedComment:
- return ErrInvalidSignature
- case errWrongFileName:
- return ErrInvalidSignature
- case errTooOld:
- return ErrTooOld
- case errWrongKey:
- return ErrInvalidSignatureUnknownKey
- }
- panic("invalid detailedVerifyErrorCode")
-}
-
-type detailedVerifyError struct {
- Code detailedVerifyErrorCode
- Message string
- Cause error
-}
-
-func (err detailedVerifyError) Error() string {
- return err.Message
-}
-func (err detailedVerifyError) Unwrap() error {
- return err.Cause
-}
-
-func (err detailedVerifyError) ToVerifyError() VerifyError {
- return VerifyError{err.Code.ToVerifyErrorCode(), err}
-}