util.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. package libtrust
  2. import (
  3. "bytes"
  4. "crypto"
  5. "crypto/elliptic"
  6. "crypto/tls"
  7. "crypto/x509"
  8. "encoding/base32"
  9. "encoding/base64"
  10. "encoding/binary"
  11. "encoding/pem"
  12. "errors"
  13. "fmt"
  14. "math/big"
  15. "net/url"
  16. "os"
  17. "path/filepath"
  18. "strings"
  19. "time"
  20. )
  21. // LoadOrCreateTrustKey will load a PrivateKey from the specified path
  22. func LoadOrCreateTrustKey(trustKeyPath string) (PrivateKey, error) {
  23. if err := os.MkdirAll(filepath.Dir(trustKeyPath), 0700); err != nil {
  24. return nil, err
  25. }
  26. trustKey, err := LoadKeyFile(trustKeyPath)
  27. if err == ErrKeyFileDoesNotExist {
  28. trustKey, err = GenerateECP256PrivateKey()
  29. if err != nil {
  30. return nil, fmt.Errorf("error generating key: %s", err)
  31. }
  32. if err := SaveKey(trustKeyPath, trustKey); err != nil {
  33. return nil, fmt.Errorf("error saving key file: %s", err)
  34. }
  35. dir, file := filepath.Split(trustKeyPath)
  36. if err := SavePublicKey(filepath.Join(dir, "public-"+file), trustKey.PublicKey()); err != nil {
  37. return nil, fmt.Errorf("error saving public key file: %s", err)
  38. }
  39. } else if err != nil {
  40. return nil, fmt.Errorf("error loading key file: %s", err)
  41. }
  42. return trustKey, nil
  43. }
  44. // NewIdentityAuthTLSClientConfig returns a tls.Config configured to use identity
  45. // based authentication from the specified dockerUrl, the rootConfigPath and
  46. // the server name to which it is connecting.
  47. // If trustUnknownHosts is true it will automatically add the host to the
  48. // known-hosts.json in rootConfigPath.
  49. func NewIdentityAuthTLSClientConfig(dockerUrl string, trustUnknownHosts bool, rootConfigPath string, serverName string) (*tls.Config, error) {
  50. tlsConfig := newTLSConfig()
  51. trustKeyPath := filepath.Join(rootConfigPath, "key.json")
  52. knownHostsPath := filepath.Join(rootConfigPath, "known-hosts.json")
  53. u, err := url.Parse(dockerUrl)
  54. if err != nil {
  55. return nil, fmt.Errorf("unable to parse machine url")
  56. }
  57. if u.Scheme == "unix" {
  58. return nil, nil
  59. }
  60. addr := u.Host
  61. proto := "tcp"
  62. trustKey, err := LoadOrCreateTrustKey(trustKeyPath)
  63. if err != nil {
  64. return nil, fmt.Errorf("unable to load trust key: %s", err)
  65. }
  66. knownHosts, err := LoadKeySetFile(knownHostsPath)
  67. if err != nil {
  68. return nil, fmt.Errorf("could not load trusted hosts file: %s", err)
  69. }
  70. allowedHosts, err := FilterByHosts(knownHosts, addr, false)
  71. if err != nil {
  72. return nil, fmt.Errorf("error filtering hosts: %s", err)
  73. }
  74. certPool, err := GenerateCACertPool(trustKey, allowedHosts)
  75. if err != nil {
  76. return nil, fmt.Errorf("Could not create CA pool: %s", err)
  77. }
  78. tlsConfig.ServerName = serverName
  79. tlsConfig.RootCAs = certPool
  80. x509Cert, err := GenerateSelfSignedClientCert(trustKey)
  81. if err != nil {
  82. return nil, fmt.Errorf("certificate generation error: %s", err)
  83. }
  84. tlsConfig.Certificates = []tls.Certificate{{
  85. Certificate: [][]byte{x509Cert.Raw},
  86. PrivateKey: trustKey.CryptoPrivateKey(),
  87. Leaf: x509Cert,
  88. }}
  89. tlsConfig.InsecureSkipVerify = true
  90. testConn, err := tls.Dial(proto, addr, tlsConfig)
  91. if err != nil {
  92. return nil, fmt.Errorf("tls Handshake error: %s", err)
  93. }
  94. opts := x509.VerifyOptions{
  95. Roots: tlsConfig.RootCAs,
  96. CurrentTime: time.Now(),
  97. DNSName: tlsConfig.ServerName,
  98. Intermediates: x509.NewCertPool(),
  99. }
  100. certs := testConn.ConnectionState().PeerCertificates
  101. for i, cert := range certs {
  102. if i == 0 {
  103. continue
  104. }
  105. opts.Intermediates.AddCert(cert)
  106. }
  107. if _, err := certs[0].Verify(opts); err != nil {
  108. if _, ok := err.(x509.UnknownAuthorityError); ok {
  109. if trustUnknownHosts {
  110. pubKey, err := FromCryptoPublicKey(certs[0].PublicKey)
  111. if err != nil {
  112. return nil, fmt.Errorf("error extracting public key from cert: %s", err)
  113. }
  114. pubKey.AddExtendedField("hosts", []string{addr})
  115. if err := AddKeySetFile(knownHostsPath, pubKey); err != nil {
  116. return nil, fmt.Errorf("error adding machine to known hosts: %s", err)
  117. }
  118. } else {
  119. return nil, fmt.Errorf("unable to connect. unknown host: %s", addr)
  120. }
  121. }
  122. }
  123. testConn.Close()
  124. tlsConfig.InsecureSkipVerify = false
  125. return tlsConfig, nil
  126. }
  127. // joseBase64UrlEncode encodes the given data using the standard base64 url
  128. // encoding format but with all trailing '=' characters ommitted in accordance
  129. // with the jose specification.
  130. // http://tools.ietf.org/html/draft-ietf-jose-json-web-signature-31#section-2
  131. func joseBase64UrlEncode(b []byte) string {
  132. return strings.TrimRight(base64.URLEncoding.EncodeToString(b), "=")
  133. }
  134. // joseBase64UrlDecode decodes the given string using the standard base64 url
  135. // decoder but first adds the appropriate number of trailing '=' characters in
  136. // accordance with the jose specification.
  137. // http://tools.ietf.org/html/draft-ietf-jose-json-web-signature-31#section-2
  138. func joseBase64UrlDecode(s string) ([]byte, error) {
  139. s = strings.Replace(s, "\n", "", -1)
  140. s = strings.Replace(s, " ", "", -1)
  141. switch len(s) % 4 {
  142. case 0:
  143. case 2:
  144. s += "=="
  145. case 3:
  146. s += "="
  147. default:
  148. return nil, errors.New("illegal base64url string")
  149. }
  150. return base64.URLEncoding.DecodeString(s)
  151. }
  152. func keyIDEncode(b []byte) string {
  153. s := strings.TrimRight(base32.StdEncoding.EncodeToString(b), "=")
  154. var buf bytes.Buffer
  155. var i int
  156. for i = 0; i < len(s)/4-1; i++ {
  157. start := i * 4
  158. end := start + 4
  159. buf.WriteString(s[start:end] + ":")
  160. }
  161. buf.WriteString(s[i*4:])
  162. return buf.String()
  163. }
  164. func keyIDFromCryptoKey(pubKey PublicKey) string {
  165. // Generate and return a 'libtrust' fingerprint of the public key.
  166. // For an RSA key this should be:
  167. // SHA256(DER encoded ASN1)
  168. // Then truncated to 240 bits and encoded into 12 base32 groups like so:
  169. // ABCD:EFGH:IJKL:MNOP:QRST:UVWX:YZ23:4567:ABCD:EFGH:IJKL:MNOP
  170. derBytes, err := x509.MarshalPKIXPublicKey(pubKey.CryptoPublicKey())
  171. if err != nil {
  172. return ""
  173. }
  174. hasher := crypto.SHA256.New()
  175. hasher.Write(derBytes)
  176. return keyIDEncode(hasher.Sum(nil)[:30])
  177. }
  178. func stringFromMap(m map[string]interface{}, key string) (string, error) {
  179. val, ok := m[key]
  180. if !ok {
  181. return "", fmt.Errorf("%q value not specified", key)
  182. }
  183. str, ok := val.(string)
  184. if !ok {
  185. return "", fmt.Errorf("%q value must be a string", key)
  186. }
  187. delete(m, key)
  188. return str, nil
  189. }
  190. func parseECCoordinate(cB64Url string, curve elliptic.Curve) (*big.Int, error) {
  191. curveByteLen := (curve.Params().BitSize + 7) >> 3
  192. cBytes, err := joseBase64UrlDecode(cB64Url)
  193. if err != nil {
  194. return nil, fmt.Errorf("invalid base64 URL encoding: %s", err)
  195. }
  196. cByteLength := len(cBytes)
  197. if cByteLength != curveByteLen {
  198. return nil, fmt.Errorf("invalid number of octets: got %d, should be %d", cByteLength, curveByteLen)
  199. }
  200. return new(big.Int).SetBytes(cBytes), nil
  201. }
  202. func parseECPrivateParam(dB64Url string, curve elliptic.Curve) (*big.Int, error) {
  203. dBytes, err := joseBase64UrlDecode(dB64Url)
  204. if err != nil {
  205. return nil, fmt.Errorf("invalid base64 URL encoding: %s", err)
  206. }
  207. // The length of this octet string MUST be ceiling(log-base-2(n)/8)
  208. // octets (where n is the order of the curve). This is because the private
  209. // key d must be in the interval [1, n-1] so the bitlength of d should be
  210. // no larger than the bitlength of n-1. The easiest way to find the octet
  211. // length is to take bitlength(n-1), add 7 to force a carry, and shift this
  212. // bit sequence right by 3, which is essentially dividing by 8 and adding
  213. // 1 if there is any remainder. Thus, the private key value d should be
  214. // output to (bitlength(n-1)+7)>>3 octets.
  215. n := curve.Params().N
  216. octetLength := (new(big.Int).Sub(n, big.NewInt(1)).BitLen() + 7) >> 3
  217. dByteLength := len(dBytes)
  218. if dByteLength != octetLength {
  219. return nil, fmt.Errorf("invalid number of octets: got %d, should be %d", dByteLength, octetLength)
  220. }
  221. return new(big.Int).SetBytes(dBytes), nil
  222. }
  223. func parseRSAModulusParam(nB64Url string) (*big.Int, error) {
  224. nBytes, err := joseBase64UrlDecode(nB64Url)
  225. if err != nil {
  226. return nil, fmt.Errorf("invalid base64 URL encoding: %s", err)
  227. }
  228. return new(big.Int).SetBytes(nBytes), nil
  229. }
  230. func serializeRSAPublicExponentParam(e int) []byte {
  231. // We MUST use the minimum number of octets to represent E.
  232. // E is supposed to be 65537 for performance and security reasons
  233. // and is what golang's rsa package generates, but it might be
  234. // different if imported from some other generator.
  235. buf := make([]byte, 4)
  236. binary.BigEndian.PutUint32(buf, uint32(e))
  237. var i int
  238. for i = 0; i < 8; i++ {
  239. if buf[i] != 0 {
  240. break
  241. }
  242. }
  243. return buf[i:]
  244. }
  245. func parseRSAPublicExponentParam(eB64Url string) (int, error) {
  246. eBytes, err := joseBase64UrlDecode(eB64Url)
  247. if err != nil {
  248. return 0, fmt.Errorf("invalid base64 URL encoding: %s", err)
  249. }
  250. // Only the minimum number of bytes were used to represent E, but
  251. // binary.BigEndian.Uint32 expects at least 4 bytes, so we need
  252. // to add zero padding if necassary.
  253. byteLen := len(eBytes)
  254. buf := make([]byte, 4-byteLen, 4)
  255. eBytes = append(buf, eBytes...)
  256. return int(binary.BigEndian.Uint32(eBytes)), nil
  257. }
  258. func parseRSAPrivateKeyParamFromMap(m map[string]interface{}, key string) (*big.Int, error) {
  259. b64Url, err := stringFromMap(m, key)
  260. if err != nil {
  261. return nil, err
  262. }
  263. paramBytes, err := joseBase64UrlDecode(b64Url)
  264. if err != nil {
  265. return nil, fmt.Errorf("invaled base64 URL encoding: %s", err)
  266. }
  267. return new(big.Int).SetBytes(paramBytes), nil
  268. }
  269. func createPemBlock(name string, derBytes []byte, headers map[string]interface{}) (*pem.Block, error) {
  270. pemBlock := &pem.Block{Type: name, Bytes: derBytes, Headers: map[string]string{}}
  271. for k, v := range headers {
  272. switch val := v.(type) {
  273. case string:
  274. pemBlock.Headers[k] = val
  275. case []string:
  276. if k == "hosts" {
  277. pemBlock.Headers[k] = strings.Join(val, ",")
  278. } else {
  279. // Return error, non-encodable type
  280. }
  281. default:
  282. // Return error, non-encodable type
  283. }
  284. }
  285. return pemBlock, nil
  286. }
  287. func pubKeyFromPEMBlock(pemBlock *pem.Block) (PublicKey, error) {
  288. cryptoPublicKey, err := x509.ParsePKIXPublicKey(pemBlock.Bytes)
  289. if err != nil {
  290. return nil, fmt.Errorf("unable to decode Public Key PEM data: %s", err)
  291. }
  292. pubKey, err := FromCryptoPublicKey(cryptoPublicKey)
  293. if err != nil {
  294. return nil, err
  295. }
  296. addPEMHeadersToKey(pemBlock, pubKey)
  297. return pubKey, nil
  298. }
  299. func addPEMHeadersToKey(pemBlock *pem.Block, pubKey PublicKey) {
  300. for key, value := range pemBlock.Headers {
  301. var safeVal interface{}
  302. if key == "hosts" {
  303. safeVal = strings.Split(value, ",")
  304. } else {
  305. safeVal = value
  306. }
  307. pubKey.AddExtendedField(key, safeVal)
  308. }
  309. }