serverlist_signature.go 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
  1. package pia
  2. import (
  3. "bytes"
  4. "crypto"
  5. "crypto/rsa"
  6. "crypto/sha256"
  7. "crypto/x509"
  8. "encoding/base64"
  9. "encoding/pem"
  10. "fmt"
  11. "unicode"
  12. )
  13. func VerifySignedServerList(raw, publicKeyPEM []byte) ([]byte, error) {
  14. jsonBody, signature, err := splitSignedServerList(raw)
  15. if err != nil {
  16. return nil, WrapError(CodeCatalogSignatureInvalid, "The PIA region list signature is missing or invalid.", err)
  17. }
  18. block, _ := pem.Decode(publicKeyPEM)
  19. if block == nil {
  20. return nil, NewError(CodeCatalogSignatureInvalid, "The built-in region-list public key is invalid.")
  21. }
  22. parsed, err := x509.ParsePKIXPublicKey(block.Bytes)
  23. if err != nil {
  24. return nil, WrapError(CodeCatalogSignatureInvalid, "The built-in region-list public key is invalid.", err)
  25. }
  26. publicKey, ok := parsed.(*rsa.PublicKey)
  27. if !ok {
  28. return nil, NewError(CodeCatalogSignatureInvalid, "The region-list public key is not RSA.")
  29. }
  30. digest := sha256.Sum256(jsonBody)
  31. if err := rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, digest[:], signature); err != nil {
  32. return nil, WrapError(CodeCatalogSignatureInvalid, "The PIA region list signature does not match its content.", err)
  33. }
  34. return jsonBody, nil
  35. }
  36. func splitSignedServerList(raw []byte) ([]byte, []byte, error) {
  37. if len(raw) == 0 || raw[0] != '{' {
  38. return nil, nil, fmt.Errorf("response does not start with a JSON object")
  39. }
  40. end := bytes.LastIndexByte(raw, '}')
  41. if end < 0 || end == len(raw)-1 {
  42. return nil, nil, fmt.Errorf("appended signature is absent")
  43. }
  44. jsonBody := append([]byte(nil), raw[:end+1]...)
  45. encoded := bytes.Map(func(r rune) rune {
  46. if unicode.IsSpace(r) {
  47. return -1
  48. }
  49. return r
  50. }, raw[end+1:])
  51. if len(encoded) == 0 {
  52. return nil, nil, fmt.Errorf("appended signature is empty")
  53. }
  54. signature := make([]byte, base64.StdEncoding.DecodedLen(len(encoded)))
  55. n, err := base64.StdEncoding.Decode(signature, encoded)
  56. if err != nil {
  57. return nil, nil, fmt.Errorf("decode signature: %w", err)
  58. }
  59. return jsonBody, signature[:n], nil
  60. }