Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

pubKeyRep.go 4.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. package dkim
  2. import (
  3. "crypto/rsa"
  4. "crypto/x509"
  5. "encoding/base64"
  6. "io/ioutil"
  7. "mime/quotedprintable"
  8. "net"
  9. "strings"
  10. )
  11. // PubKeyRep represents a parsed version of public key record
  12. type PubKeyRep struct {
  13. Version string
  14. HashAlgo []string
  15. KeyType string
  16. Note string
  17. PubKey rsa.PublicKey
  18. ServiceType []string
  19. FlagTesting bool // flag y
  20. FlagIMustBeD bool // flag i
  21. }
  22. // DNSOptions holds settings for looking up DNS records
  23. type DNSOptions struct {
  24. netLookupTXT func(name string) ([]string, error)
  25. }
  26. // DNSOpt represents an optional setting for looking up DNS records
  27. type DNSOpt interface {
  28. apply(*DNSOptions)
  29. }
  30. type dnsOpt func(*DNSOptions)
  31. func (opt dnsOpt) apply(dnsOpts *DNSOptions) {
  32. opt(dnsOpts)
  33. }
  34. // DNSOptLookupTXT sets the function to use to lookup TXT records.
  35. //
  36. // This should probably only be used in tests.
  37. func DNSOptLookupTXT(netLookupTXT func(name string) ([]string, error)) DNSOpt {
  38. return dnsOpt(func(opts *DNSOptions) {
  39. opts.netLookupTXT = netLookupTXT
  40. })
  41. }
  42. // NewPubKeyRespFromDNS retrieves the TXT record from DNS based on the specified domain and selector
  43. // and parses it.
  44. func NewPubKeyRespFromDNS(selector, domain string, opts ...DNSOpt) (*PubKeyRep, verifyOutput, error) {
  45. dnsOpts := DNSOptions{}
  46. for _, opt := range opts {
  47. opt.apply(&dnsOpts)
  48. }
  49. if dnsOpts.netLookupTXT == nil {
  50. dnsOpts.netLookupTXT = net.LookupTXT
  51. }
  52. txt, err := dnsOpts.netLookupTXT(selector + "._domainkey." + domain)
  53. if err != nil {
  54. if strings.HasSuffix(err.Error(), "no such host") {
  55. return nil, PERMFAIL, ErrVerifyNoKeyForSignature
  56. }
  57. return nil, TEMPFAIL, ErrVerifyKeyUnavailable
  58. }
  59. // empty record
  60. if len(txt) == 0 {
  61. return nil, PERMFAIL, ErrVerifyNoKeyForSignature
  62. }
  63. // parsing, we keep the first record
  64. // TODO: if there is multiple record
  65. return NewPubKeyResp(txt[0])
  66. }
  67. // NewPubKeyResp parses DKIM record (usually from DNS)
  68. func NewPubKeyResp(dkimRecord string) (*PubKeyRep, verifyOutput, error) {
  69. pkr := new(PubKeyRep)
  70. pkr.Version = "DKIM1"
  71. pkr.HashAlgo = []string{"sha1", "sha256"}
  72. pkr.KeyType = "rsa"
  73. pkr.FlagTesting = false
  74. pkr.FlagIMustBeD = false
  75. p := strings.Split(dkimRecord, ";")
  76. for i, data := range p {
  77. keyVal := strings.SplitN(data, "=", 2)
  78. val := ""
  79. if len(keyVal) > 1 {
  80. val = strings.TrimSpace(keyVal[1])
  81. }
  82. switch strings.ToLower(strings.TrimSpace(keyVal[0])) {
  83. case "v":
  84. // RFC: is this tag is specified it MUST be the first in the record
  85. if i != 0 {
  86. return nil, PERMFAIL, ErrVerifyTagVMustBeTheFirst
  87. }
  88. pkr.Version = val
  89. if pkr.Version != "DKIM1" {
  90. return nil, PERMFAIL, ErrVerifyVersionMusBeDkim1
  91. }
  92. case "h":
  93. p := strings.Split(strings.ToLower(val), ":")
  94. pkr.HashAlgo = []string{}
  95. for _, h := range p {
  96. h = strings.TrimSpace(h)
  97. if h == "sha1" || h == "sha256" {
  98. pkr.HashAlgo = append(pkr.HashAlgo, h)
  99. }
  100. }
  101. // if empty switch back to default
  102. if len(pkr.HashAlgo) == 0 {
  103. pkr.HashAlgo = []string{"sha1", "sha256"}
  104. }
  105. case "k":
  106. if strings.ToLower(val) != "rsa" {
  107. return nil, PERMFAIL, ErrVerifyBadKeyType
  108. }
  109. case "n":
  110. qp, err := ioutil.ReadAll(quotedprintable.NewReader(strings.NewReader(val)))
  111. if err == nil {
  112. val = string(qp)
  113. }
  114. pkr.Note = val
  115. case "p":
  116. rawkey := val
  117. if rawkey == "" {
  118. return nil, PERMFAIL, ErrVerifyRevokedKey
  119. }
  120. un64, err := base64.StdEncoding.DecodeString(rawkey)
  121. if err != nil {
  122. return nil, PERMFAIL, ErrVerifyBadKey
  123. }
  124. pk, err := x509.ParsePKIXPublicKey(un64)
  125. if pk, ok := pk.(*rsa.PublicKey); ok {
  126. pkr.PubKey = *pk
  127. }
  128. case "s":
  129. t := strings.Split(strings.ToLower(val), ":")
  130. for _, tt := range t {
  131. tt = strings.TrimSpace(tt)
  132. switch tt {
  133. case "*":
  134. pkr.ServiceType = append(pkr.ServiceType, "all")
  135. case "email":
  136. pkr.ServiceType = append(pkr.ServiceType, tt)
  137. }
  138. }
  139. case "t":
  140. flags := strings.Split(strings.ToLower(val), ":")
  141. for _, flag := range flags {
  142. flag = strings.TrimSpace(flag)
  143. switch flag {
  144. case "y":
  145. pkr.FlagTesting = true
  146. case "s":
  147. pkr.FlagIMustBeD = true
  148. }
  149. }
  150. }
  151. }
  152. // if no pubkey
  153. if pkr.PubKey == (rsa.PublicKey{}) {
  154. return nil, PERMFAIL, ErrVerifyNoKey
  155. }
  156. // No service type
  157. if len(pkr.ServiceType) == 0 {
  158. pkr.ServiceType = []string{"all"}
  159. }
  160. return pkr, SUCCESS, nil
  161. }