// Opens a file to read the configuration elements from. // fn: Filename to open. // password: Password to encrypt and decrypt secure content. // Has to be either, 16, 24, or 32 in length. func Open(fn, password string) (*Configurator, error) { pl := len(password) switch pl { default: return nil, aes.KeySizeError(pl) case 16, 24, 32: break } f, err := os.Open(fn) if err != nil { return nil, err } defer f.Close() scanner := bufio.NewScanner(f) lines := make(map[string]string) for i := 1; scanner.Scan(); i++ { str := strings.Split(scanner.Text(), "=") lines[str[0]] = str[1] } block, err := aes.NewCipher([]byte(password)) if err != nil { return nil, err } return &Configurator{ filename: fn, lines: lines, cipherBlock: block, decrypted: make(map[string]string), }, nil }
// Read checks the validity of a file and attempts to decrypt it. func (m *MutableFile) Read(p []byte) (n int, err error) { // delegate VK checking to Verify() if m.Cap.rk == nil { return 0, CapabilityError } ok, err := m.Verify() if err != nil { return 0, err } if !ok { return 0, rsa.ErrVerification } // TODO don't repeat this file read filePath := path.Join(m.storageDir, m.filename) rawFile, err := ioutil.ReadFile(filePath) if err != nil { return 0, err } sigSize := AsymmetricKeySize / 8 sigOffset := len(rawFile) - sigSize // for generating EK from RK fileSalt := rawFile[:SaltSize] ek := truncHash(TagEKFromSaltRK, append(m.Cap.rk, fileSalt...), SymmetricKeySize) if ek == nil || len(ek) != SymmetricKeySize { return 0, aes.KeySizeError(len(ek)) } block, err := aes.NewCipher(ek) if err != nil { return 0, err } gcm, err := cipher.NewGCM(block) if err != nil { return 0, err } // skip salt, get nonce, stop before ciphertext nonce := rawFile[SaltSize : SaltSize+gcm.NonceSize()] // skip salt and nonce, get ciphertext|tag, stop before sig ciphertext := rawFile[SaltSize+gcm.NonceSize() : sigOffset] decryptBytes, err := gcm.Open(nil, nonce, ciphertext, nil) if err != nil { return 0, nil } if cap(p) < len(decryptBytes) { return copy(p, decryptBytes[:cap(p)]), io.ErrShortBuffer } else { return copy(p, decryptBytes), io.EOF } }
// Write writes a file. Format is // { // fileSalt [16]byte // gcmNonce [12]byte // ciphertext [len(contents)]byte // gcmTag [16]byte // signature [256]byte // } func (m *MutableFile) Write(data []byte) (n int, err error) { // TODO check capability derivation chains if m.Cap.sk == nil || m.Cap.rk == nil || m.salt == nil { return 0, CapabilityError } ek := truncHash(TagEKFromSaltRK, append(m.Cap.rk, m.salt...), SymmetricKeySize) if ek == nil || len(ek) != SymmetricKeySize { return 0, aes.KeySizeError(len(ek)) } block, err := aes.NewCipher(ek) if err != nil { return 0, err } gcm, err := cipher.NewGCM(block) if err != nil { return 0, err } buf := make([]byte, len(m.salt)) copy(buf, m.salt) nonce := make([]byte, gcm.NonceSize()) n, err = rand.Read(nonce) if n != gcm.NonceSize() || err != nil { panic("rand.Read failed!1!!") } buf = append(buf, nonce...) buf = gcm.Seal(buf, nonce, data, nil) // remember that this is hashing all of salt|nonce|ciphertext|tag hashed := sha256.Sum256(buf) sig, err := rsa.SignPKCS1v15(rand.Reader, m.Cap.sk, crypto.SHA256, hashed[:]) if err != nil { return 0, err } out, err := os.Create(path.Join(m.storageDir, m.filename)) if err != nil { return 0, err } defer out.Close() bufN, err := out.Write(buf) if err != nil { return 0, err } sigN, err := out.Write(sig) if err != nil { return 0, err } return bufN + sigN, nil }
func TestCreateEndpoint(t *testing.T) { useMockFuncs() defer useStdFuncs() mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() mckEndHandler := NewMockHandler(mockCtrl) Convey("Should allocate update endpoints", t, func() { app := NewApplication() app.endpointTemplate = testEndpointTemplate Convey("Should not encrypt endpoints without a key", func() { app.SetTokenKey("") app.SetEndpointHandler(mckEndHandler) mckEndHandler.EXPECT().URL().Return("https://example.com") endpoint, err := app.CreateEndpoint("123") So(err, ShouldBeNil) So(endpoint, ShouldEqual, "https://example.com/123") }) Convey("Should encrypt endpoints with a key", func() { app.SetTokenKey("HVozKz_n-DPopP5W877DpRKQOW_dylVf") app.SetEndpointHandler(mckEndHandler) mckEndHandler.EXPECT().URL().Return("https://example.com") endpoint, err := app.CreateEndpoint("456") So(err, ShouldBeNil) So(endpoint, ShouldEqual, "https://example.com/AAAAAAAAAAAAAAAAAAAAAGMKig==") }) Convey("Should reject invalid keys", func() { app.SetTokenKey("lLyhlLk8qus1ky4ER8yjN5o=") app.SetEndpointHandler(mckEndHandler) _, err := app.CreateEndpoint("123") So(err, ShouldEqual, aes.KeySizeError(17)) }) Convey("Should return a relative URL without an update handler", func() { app.SetTokenKey("O03rpLsdafhIhJEjEJt-CgVHyqHI650oy0pZZvplKDc=") endpoint, err := app.CreateEndpoint("789") So(err, ShouldBeNil) So(endpoint, ShouldEqual, "/AAAAAAAAAAAAAAAAAAAAAPfdsA==") }) }) }
func Test_Decode(t *testing.T) { var ( decrypted, expected []byte err error ) key := []byte{0xf8, 0x59, 0x4, 0x72, 0x1c, 0xa, 0xc, 0x85, 0x5b, 0x7a, 0x61, 0x26, 0xa5, 0x5a, 0xe2, 0x3b} encrypted := "6MgxfnBKjWtSNm6Q9WunFbj2hcjmeDudKuWUAeU=" if decrypted, err = Decode(key, encrypted); err != nil { t.Errorf("Error decoding value: %s", err) } expected = []byte("Hello, world!") if !bytes.Equal(decrypted, expected) { t.Errorf("Unexpected result decoding with key: want %#v; got %#v", expected, decrypted) } if _, err = Decode(key[:14], "6MgxfnBKjWtSNm6Q9WunFbj2hcjmeDudKuWUAeU="); err != aes.KeySizeError(14) { t.Errorf("Invalid key size: want aes.KeySizeError(14); got %s", err) } if _, err = Decode(key, "!@#$%^&*()-+[]{}"); err != base64.CorruptInputError(0) { t.Errorf("Invalid Base64: want base64.CorruptInputError(0); got %s", err) } if _, err = Decode(key, encrypted[:8]); err != ValueSizeError(6) { t.Errorf("Encrypted value too short: want ValueSizeError(6); got %s", err) } if decrypted, err = Decode(nil, encrypted); err != nil { t.Errorf("Error decoding without key: %s", err) } expected = []byte(encrypted) if !bytes.Equal(decrypted, expected) { t.Errorf("Unexpected result decoding without key: want %#v; got %#v", expected, decrypted) } if decrypted, err = Decode(key, ""); err != nil { t.Errorf("Error decoding empty string: %s", err) } if len(decrypted) != 0 { t.Errorf("Unexpected result decoding empty string: got %#v", decrypted) } // Empty payload with valid IV. if decrypted, err = Decode(key, "dEmnrPZHgiOgttx5lhkx4w=="); err != nil { t.Errorf("Error decoding empty payload: %s", err) } if len(decrypted) != 0 { t.Errorf("Unexpected result decoding empty payload: got %#v", decrypted) } }
// Decrypt decrypts data using 256-bit AES-GCM. This both hides the content of // the data and provides a check that it hasn't been altered. Expects input // form nonce|ciphertext|tag where '|' indicates concatenation. func Decrypt(ciphertext, key []byte) (plaintext []byte, err error) { if len(key) != aesKeySize { return nil, aes.KeySizeError(len(key)) } aes, err := aes.NewCipher(key) if err != nil { return nil, err } gcm, err := cipher.NewGCM(aes) if err != nil { return nil, err } return gcm.Open(nil, ciphertext[:gcm.NonceSize()], ciphertext[gcm.NonceSize():], nil) }
// Encrypt encrypts data using 256-bit AES-GCM. This both hides the content of // the data and provides a check that it hasn't been altered. Output takes the // form nonce|ciphertext|tag where '|' indicates concatenation. func Encrypt(plaintext, key []byte) (ciphertext []byte, err error) { if len(key) != aesKeySize { return nil, aes.KeySizeError(len(key)) } aes, err := aes.NewCipher(key) if err != nil { return nil, err } gcm, err := cipher.NewGCM(aes) if err != nil { return nil, err } nonce, err := generateBytes(gcm.NonceSize()) if err != nil { return nil, err } return gcm.Seal(nonce, nonce, plaintext, nil), nil }
// NewAes256Gcm creates new Aes256Gcm with key func NewAes256Gcm(key []byte) (*Aes256Gcm, error) { if len(key) != KeySize { return nil, aes.KeySizeError(len(key)) } return &Aes256Gcm{key: key}, nil }