Merge pull request #1154 from shawnzhu/RSA-OAEP

AES -> RSA-OAEP
This commit is contained in:
Brad Rydzewski 2015-08-19 23:14:28 -07:00
commit 7fb90fccbb
7 changed files with 122 additions and 125 deletions

View file

@ -183,7 +183,7 @@ func RunBuild(c *gin.Context) {
if repo.Params != nil && len(repo.Params) != 0 { if repo.Params != nil && len(repo.Params) != 0 {
raw = []byte(inject.InjectSafe(string(raw), repo.Params)) raw = []byte(inject.InjectSafe(string(raw), repo.Params))
} }
encrypted, _ := secure.Parse(repo.Hash, string(raw)) encrypted, _ := secure.Parse(repo.Keys.Private, repo.Hash, string(raw))
if encrypted != nil && len(encrypted) != 0 { if encrypted != nil && len(encrypted) != 0 {
raw = []byte(inject.InjectSafe(string(raw), encrypted)) raw = []byte(inject.InjectSafe(string(raw), encrypted))
} }

View file

@ -101,7 +101,7 @@ func PostHook(c *gin.Context) {
if repo.Params != nil && len(repo.Params) != 0 { if repo.Params != nil && len(repo.Params) != 0 {
raw = []byte(inject.InjectSafe(string(raw), repo.Params)) raw = []byte(inject.InjectSafe(string(raw), repo.Params))
} }
encrypted, _ := secure.Parse(repo.Hash, string(raw)) encrypted, _ := secure.Parse(repo.Keys.Private, repo.Hash, string(raw))
if encrypted != nil && len(encrypted) != 0 { if encrypted != nil && len(encrypted) != 0 {
raw = []byte(inject.InjectSafe(string(raw), encrypted)) raw = []byte(inject.InjectSafe(string(raw), encrypted))
} }

View file

@ -253,7 +253,8 @@ func Encrypt(c *gin.Context) {
in := map[string]string{} in := map[string]string{}
json.NewDecoder(c.Request.Body).Decode(&in) json.NewDecoder(c.Request.Body).Decode(&in)
err := secure.EncryptMap(repo.Hash, in) privKey := sshutil.UnMarshalPrivateKey([]byte(repo.Keys.Private))
err := secure.EncryptMap(secure.ToHash(repo.Hash), &privKey.PublicKey, in)
if err != nil { if err != nil {
c.Fail(500, err) c.Fail(500, err)
return return
@ -261,7 +262,7 @@ func Encrypt(c *gin.Context) {
c.JSON(200, &in) c.JSON(200, &in)
} }
// Unubscribe accapets a request to unsubscribe the // Unsubscribe accapets a request to unsubscribe the
// currently authenticated user to the repository. // currently authenticated user to the repository.
// //
// DEL /api/subscribers/:owner/:name // DEL /api/subscribers/:owner/:name

View file

@ -4,7 +4,9 @@ import (
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
"crypto/x509" "crypto/x509"
"encoding/base64"
"encoding/pem" "encoding/pem"
"hash"
"github.com/drone/drone/Godeps/_workspace/src/code.google.com/p/go.crypto/ssh" "github.com/drone/drone/Godeps/_workspace/src/code.google.com/p/go.crypto/ssh"
) )
@ -38,15 +40,35 @@ func MarshalPrivateKey(privkey *rsa.PrivateKey) []byte {
return privateKeyPEM return privateKeyPEM
} }
// helper function to encrypt a plain-text string using // UnMarshalPrivateKey is a helper function that unmarshals a PEM
// an RSA public key. // bytes to an RSA Private Key
func Encrypt(pubkey *rsa.PublicKey, msg string) ([]byte, error) { func UnMarshalPrivateKey(privateKeyPEM []byte) *rsa.PrivateKey {
return rsa.EncryptPKCS1v15(rand.Reader, pubkey, []byte(msg)) derBlock, _ := pem.Decode(privateKeyPEM)
privateKey, err := x509.ParsePKCS1PrivateKey(derBlock.Bytes)
if err != nil {
return nil
}
return privateKey
} }
// helper function to encrypt a plain-text string using // Encrypt is helper function to encrypt a plain-text string using
// an RSA public key. // an RSA public key.
func Decrypt(privkey *rsa.PrivateKey, secret string) (string, error) { func Encrypt(hash hash.Hash, pubkey *rsa.PublicKey, msg string) (string, error) {
msg, err := rsa.DecryptPKCS1v15(rand.Reader, privkey, []byte(secret)) src, err := rsa.EncryptOAEP(hash, rand.Reader, pubkey, []byte(msg), nil)
return string(msg), err
return base64.StdEncoding.EncodeToString(src), err
}
// Decrypt is helper function to encrypt a plain-text string using
// an RSA public key.
func Decrypt(hash hash.Hash, privkey *rsa.PrivateKey, secret string) (string, error) {
decoded, err := base64.StdEncoding.DecodeString(secret)
if err != nil {
return "", err
}
out, err := rsa.DecryptOAEP(hash, rand.Reader, privkey, decoded, nil)
return string(out), err
} }

View file

@ -0,0 +1,40 @@
package sshutil
import (
"crypto/sha256"
"testing"
"github.com/drone/drone/Godeps/_workspace/src/github.com/franela/goblin"
)
func TestSSHUtil(t *testing.T) {
g := goblin.Goblin(t)
g.Describe("sshutil", func() {
var encrypted, testMsg string
privkey, err := GeneratePrivateKey()
g.Assert(err == nil).IsTrue()
pubkey := privkey.PublicKey
sha256 := sha256.New()
testMsg = "foo=bar"
g.Before(func() {
encrypted, err = Encrypt(sha256, &pubkey, testMsg)
g.Assert(err == nil).IsTrue()
})
g.It("Can decrypt encrypted msg", func() {
decrypted, err := Decrypt(sha256, privkey, encrypted)
g.Assert(err == nil).IsTrue()
g.Assert(decrypted == testMsg).IsTrue()
})
g.It("Unmarshals private key from PEM block", func() {
privateKeyPEM := MarshalPrivateKey(privkey)
privateKey := UnMarshalPrivateKey(privateKeyPEM)
g.Assert(privateKey.PublicKey.E == pubkey.E).IsTrue()
})
})
}

View file

@ -1,79 +1,37 @@
package secure package secure
import ( import (
"crypto/aes" "crypto/rsa"
"crypto/cipher" "crypto/sha256"
"crypto/rand" "hash"
"encoding/base64"
"fmt"
"io"
"github.com/drone/drone/Godeps/_workspace/src/gopkg.in/yaml.v2" "github.com/drone/drone/Godeps/_workspace/src/gopkg.in/yaml.v2"
"github.com/drone/drone/pkg/utils/sshutil"
) )
// Parse parses and returns the secure section of the // Parse parses and returns the secure section of the
// yaml file as plaintext parameters. // yaml file as plaintext parameters.
func Parse(key, raw string) (map[string]string, error) { func Parse(privateKeyPEM, repoHash, raw string) (map[string]string, error) {
params, err := parseSecure(raw) params, err := parseSecure(raw)
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = DecryptMap(key, params)
hasher := ToHash(repoHash)
privKey := sshutil.UnMarshalPrivateKey([]byte(privateKeyPEM))
err = DecryptMap(hasher, privKey, params)
return params, err return params, err
} }
// Encrypt encrypts a string to base64 crypto using AES. // DecryptMap decrypts values of a map of named parameters
func Encrypt(key, text string) (_ string, err error) { // from base64 to decrypted strings.
plaintext := []byte(text) func DecryptMap(hasher hash.Hash, privKey *rsa.PrivateKey, params map[string]string) error {
block, err := aes.NewCipher(trimKey(key))
if err != nil {
return
}
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
iv := ciphertext[:aes.BlockSize]
if _, err = io.ReadFull(rand.Reader, iv); err != nil {
return
}
stream := cipher.NewCFBEncrypter(block, iv)
stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)
return base64.URLEncoding.EncodeToString(ciphertext), nil
}
// Decrypt decrtyps from base64 to decrypted string.
func Decrypt(key, text string) (_ string, err error) {
ciphertext, err := base64.URLEncoding.DecodeString(text)
if err != nil {
return
}
block, err := aes.NewCipher(trimKey(key))
if err != nil {
return
}
if len(ciphertext) < aes.BlockSize {
err = fmt.Errorf("ciphertext too short")
return
}
iv := ciphertext[:aes.BlockSize]
ciphertext = ciphertext[aes.BlockSize:]
stream := cipher.NewCFBDecrypter(block, iv)
stream.XORKeyStream(ciphertext, ciphertext)
return fmt.Sprintf("%s", ciphertext), nil
}
// DecryptMap decrypts a map of named parameters
// from base64 to decrypted string.
func DecryptMap(key string, params map[string]string) error {
var err error var err error
for name, value := range params {
params[name], err = Decrypt(key, value) for name, encrypted := range params {
params[name], err = sshutil.Decrypt(hasher, privKey, encrypted)
if err != nil { if err != nil {
return err return err
} }
@ -81,12 +39,12 @@ func DecryptMap(key string, params map[string]string) error {
return nil return nil
} }
// EncryptMap encrypts encrypts a map of string parameters // EncryptMap encrypts values of a map of named parameters
// to base64 crypto using AES. func EncryptMap(hasher hash.Hash, pubKey *rsa.PublicKey, params map[string]string) error {
func EncryptMap(key string, params map[string]string) error {
var err error var err error
for name, value := range params { for name, value := range params {
params[name], err = Encrypt(key, value) params[name], err = sshutil.Encrypt(hasher, pubKey, value)
if err != nil { if err != nil {
return err return err
} }
@ -94,22 +52,21 @@ func EncryptMap(key string, params map[string]string) error {
return nil return nil
} }
// helper function that trims a key to a maximum // parseSecure is helper function to parse the Secure data from
// of 32 bytes to match the expected AES block size.
func trimKey(key string) []byte {
b := []byte(key)
if len(b) > 32 {
b = b[:32]
}
return b
}
// helper function to parse the Secure data from
// the raw yaml file. // the raw yaml file.
func parseSecure(raw string) (map[string]string, error) { func parseSecure(raw string) (map[string]string, error) {
data := struct { data := struct {
Secure map[string]string Secure map[string]string
}{} }{}
err := yaml.Unmarshal([]byte(raw), &data) err := yaml.Unmarshal([]byte(raw), &data)
return data.Secure, err return data.Secure, err
} }
// ToHash is helper function to generate Hash of given string
func ToHash(key string) hash.Hash {
hasher := sha256.New()
hasher.Write([]byte(key))
hasher.Reset()
return hasher
}

View file

@ -4,58 +4,35 @@ import (
"testing" "testing"
"github.com/drone/drone/Godeps/_workspace/src/github.com/franela/goblin" "github.com/drone/drone/Godeps/_workspace/src/github.com/franela/goblin"
"github.com/drone/drone/pkg/utils/sshutil"
) )
func Test_Secure(t *testing.T) { func Test_Secure(t *testing.T) {
g := goblin.Goblin(t) g := goblin.Goblin(t)
g.Describe("Encrypt params", func() { g.Describe("Encrypt params", func() {
privKey, _ := sshutil.GeneratePrivateKey()
publicKey := &privKey.PublicKey
key := "9T2tH3qZ8FSPr9uxrhzV4mn2VdVgA56xPVtYvCh0" privateKeyPEM := string(sshutil.MarshalPrivateKey(privKey))
repoHash := "9T2tH3qZ8FSPr9uxrhzV4mn2VdVgA56xPVtYvCh0"
hashKey := ToHash(repoHash)
text := "super_duper_secret" text := "super_duper_secret"
long := "-----BEGIN RSA PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\nDEK-Info: DES-EDE3-CBC,32495A90F3FF199D\nlrMAsSjjkKiRxGdgR8p5kZJj0AFgdWYa3OT2snIXnN5+/p7j13PSkseUcrAFyokc\nV9pgeDfitAhb9lpdjxjjuxRcuQjBfmNVLPF9MFyNOvhrprGNukUh/12oSKO9dFEt\ns39F/2h6Ld5IQrGt3gZaBB1aGO+tw3ill1VBy2zGPIDeuSz6DS3GG/oQ2gLSSMP4\nOVfQ32Oajo496iHRkdIh/7Hho7BNzMYr1GxrYTcE9/Znr6xgeSdNT37CCeCH8cmP\naEAUgSMTeIMVSpILwkKeNvBURic1EWaqXRgPRIWK0vNyOCs/+jNoFISnV4pu1ROF\n92vayHDNSVw9wHcdSQ75XSE4Msawqv5U1iI7e2lD64uo1qhmJdrPcXDJQCiDbh+F\nhQhF+wAoLRvMNwwhg+LttL8vXqMDQl3olsWSvWPs6b/MZpB0qwd1bklzA6P+PeAU\nsfOvTqi9edIOfKqvXqTXEhBP8qC7ZtOKLGnryZb7W04SSVrNtuJUFRcLiqu+w/F/\nMSxGSGalYpzIZ1B5HLQqISgWMXdbt39uMeeooeZjkuI3VIllFjtybecjPR9ZYQPt\nFFEP1XqNXjLFmGh84TXtvGLWretWM1OZmN8UKKUeATqrr7zuh5AYGAIbXd8BvweL\nPigl9ei0hTculPqohvkoc5x1srPBvzHrirGlxOYjW3fc4kDgZpy+6ik5k5g7JWQD\nlbXCRz3HGazgUPeiwUr06a52vhgT7QuNIUZqdHb4IfCYs2pQTLHzQjAqvVk1mm2D\nkh4myIcTtf69BFcu/Wuptm3NaKd1nwk1squR6psvcTXOWII81pstnxNYkrokx4r2\n7YVllNruOD+cMDNZbIG2CwT6V9ukIS8tl9EJp8eyb0a1uAEc22BNOjYHPF50beWF\nukf3uc0SA+G3zhmXCM5sMf5OxVjKr5jgcir7kySY5KbmG71omYhczgr4H0qgxYo9\nZyj2wMKrTHLfFOpd4OOEun9Gi3srqlKZep7Hj7gNyUwZu1qiBvElmBVmp0HJxT0N\nmktuaVbaFgBsTS0/us1EqWvCA4REh1Ut/NoA9oG3JFt0lGDstTw1j+orDmIHOmSu\n7FKYzr0uCz14AkLMSOixdPD1F0YyED1NMVnRVXw77HiAFGmb0CDi2KEg70pEKpn3\nksa8oe0MQi6oEwlMsAxVTXOB1wblTBuSBeaECzTzWE+/DHF+QQfQi8kAjjSdmmMJ\nyN+shdBWHYRGYnxRkTatONhcDBIY7sZV7wolYHz/rf7dpYUZf37vdQnYV8FpO1um\nYa0GslyRJ5GqMBfDS1cQKne+FvVHxEE2YqEGBcOYhx/JI2soE8aA8W4XffN+DoEy\nZkinJ/+BOwJ/zUI9GZtwB4JXqbNEE+j7r7/fJO9KxfPp4MPK4YWu0H0EUWONpVwe\nTWtbRhQUCOe4PVSC/Vv1pstvMD/D+E/0L4GQNHxr+xyFxuvILty5lvFTxoAVYpqD\nu8gNhk3NWefTrlSkhY4N+tPP6o7E4t3y40nOA/d9qaqiid+lYcIDB0cJTpZvgeeQ\nijohxY3PHruU4vVZa37ITQnco9az6lsy18vbU0bOyK2fEZ2R9XVO8fH11jiV8oGH\n-----END RSA PRIVATE KEY-----" encryptedValue, _ := sshutil.Encrypt(hashKey, publicKey, text)
g.It("Should encrypt a string", func() {
encrypted, err := Encrypt(key, text)
g.Assert(err == nil).IsTrue()
decrypted, err := Decrypt(key, encrypted)
g.Assert(err == nil).IsTrue()
g.Assert(text).Equal(decrypted)
})
g.It("Should encrypt a long string", func() {
encrypted, err := Encrypt(key, long)
g.Assert(err == nil).IsTrue()
decrypted, err := Decrypt(key, encrypted)
g.Assert(err == nil).IsTrue()
g.Assert(long).Equal(decrypted)
})
g.It("Should decrypt a map", func() {
params := map[string]string{
"foo": "2NQPoQfxPERVi42OEYzuVTjQrEQSrcN2-Pwk4kTlIVN5HA==",
}
err := DecryptMap(key, params)
g.Assert(err == nil).IsTrue()
g.Assert(params["foo"]).Equal("super_duper_secret")
})
g.It("Should trim a key with blocksize greater than 32 bytes", func() {
trimmed := trimKey("9T2tH3qZ8FSPr9uxrhzV4mn2VdVgA56x")
g.Assert(len(key) > 32).IsTrue()
g.Assert(len(trimmed)).Equal(32)
})
g.It("Should decrypt a yaml", func() { g.It("Should decrypt a yaml", func() {
yaml := `secure: {"foo": "2NQPoQfxPERVi42OEYzuVTjQrEQSrcN2-Pwk4kTlIVN5HA=="}` yaml := "secure: {\"foo\": \"" + encryptedValue + "\"}"
decrypted, err := Parse(key, yaml) decrypted, err := Parse(privateKeyPEM, repoHash, yaml)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(decrypted["foo"]).Equal("super_duper_secret") g.Assert(decrypted["foo"]).Equal(text)
}) })
g.It("Should decrypt a yaml with no secure section", func() { g.It("Should decrypt a yaml with no secure section", func() {
yaml := `foo: bar` yaml := `foo: bar`
decrypted, err := Parse(key, yaml) decrypted, err := Parse(privateKeyPEM, repoHash, yaml)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(len(decrypted)).Equal(0) g.Assert(len(decrypted)).Equal(0)
}) })
@ -64,10 +41,10 @@ func Test_Secure(t *testing.T) {
params := map[string]string{ params := map[string]string{
"foo": text, "foo": text,
} }
err := EncryptMap(key, params) err := EncryptMap(hashKey, publicKey, params)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(params["foo"] == "super_duper_secret").IsFalse() g.Assert(params["foo"] == "super_duper_secret").IsFalse()
err = DecryptMap(key, params) err = DecryptMap(hashKey, privKey, params)
g.Assert(err == nil).IsTrue() g.Assert(err == nil).IsTrue()
g.Assert(params["foo"] == "super_duper_secret").IsTrue() g.Assert(params["foo"] == "super_duper_secret").IsTrue()
}) })