Commit 77eae37d authored by ale's avatar ale

Use the new go-common/userenckey package

parent 0cdedceb
......@@ -3,7 +3,7 @@ package accountserver
import (
"context"
"git.autistici.org/id/keystore/userenckey"
"git.autistici.org/ai3/go-common/userenckey"
)
func keysToBytes(keys []*UserEncryptionKey) [][]byte {
......@@ -14,7 +14,7 @@ func keysToBytes(keys []*UserEncryptionKey) [][]byte {
return rawKeys
}
func (s *AccountService) initializeEncryptionKeys(ctx context.Context, tx TX, user *User, password string) (keys []*UserEncryptionKey, decrypted []byte, err error) {
func (s *AccountService) initializeEncryptionKeys(ctx context.Context, tx TX, user *User, password string) (keys []*UserEncryptionKey, decrypted *userenckey.Key, err error) {
// Create new keys
pub, priv, err := userenckey.GenerateKey()
if err != nil {
......@@ -37,7 +37,7 @@ func (s *AccountService) initializeEncryptionKeys(ctx context.Context, tx TX, us
return
}
func (s *AccountService) readOrInitializeEncryptionKeys(ctx context.Context, tx TX, user *User, oldPassword, newPassword string) (keys []*UserEncryptionKey, decrypted []byte, err error) {
func (s *AccountService) readOrInitializeEncryptionKeys(ctx context.Context, tx TX, user *User, oldPassword, newPassword string) (keys []*UserEncryptionKey, decrypted *userenckey.Key, err error) {
if user.HasEncryptionKeys {
// Fetch the encryption keys from the database.
keys, err = tx.GetUserEncryptionKeys(ctx, user)
......@@ -50,7 +50,7 @@ func (s *AccountService) readOrInitializeEncryptionKeys(ctx context.Context, tx
return s.initializeEncryptionKeys(ctx, tx, user, newPassword)
}
func updateEncryptionKey(keys []*UserEncryptionKey, decrypted []byte, keyID, password string) ([]*UserEncryptionKey, error) {
func updateEncryptionKey(keys []*UserEncryptionKey, decrypted *userenckey.Key, keyID, password string) ([]*UserEncryptionKey, error) {
encrypted, err := userenckey.Encrypt(decrypted, []byte(password))
if err != nil {
return nil, err
......
This diff is collapsed.
package userenckey
import (
"errors"
"github.com/miscreant/miscreant/go"
"golang.org/x/crypto/scrypt"
)
// ErrBadPassword is returned on decryption failure.
var ErrBadPassword = errors.New("could not decrypt key with password")
const aeadAlgo = "AES-SIV"
const (
scryptN = 32768
scryptR = 8
scryptP = 1
keyLen = 64
saltLen = 32
)
// Decrypt one out of multiple keys with the specified password. The
// keys share the same cleartext, but have been encrypted with
// different passwords.
func Decrypt(encKeys [][]byte, pw []byte) ([]byte, error) {
for _, key := range encKeys {
dec, err := decryptData(key, pw)
if err == nil {
return dec, nil
}
}
return nil, ErrBadPassword
}
func kdf(pw, salt []byte) ([]byte, error) {
return scrypt.Key(pw, salt, scryptN, scryptR, scryptP, keyLen)
}
func decryptData(data, pw []byte) ([]byte, error) {
// The KDF salt is prepended to the encrypted key.
if len(data) < saltLen {
return nil, errors.New("short data")
}
salt := data[:saltLen]
data = data[saltLen:]
// Apply the key derivation function to the password to obtain
// a 64 byte key.
dk, err := kdf(pw, salt)
if err != nil {
return nil, err
}
// Set up the AES-SIV secret box.
cipher, err := miscreant.NewAEAD(aeadAlgo, dk, 0)
if err != nil {
return nil, err
}
return cipher.Open(nil, nil, data, nil)
}
package userenckey
import (
"crypto/rand"
"io"
"github.com/miscreant/miscreant/go"
)
// Encrypt a key with a password and a random salt.
func Encrypt(key, pw []byte) ([]byte, error) {
salt := genRandomSalt()
dk, err := kdf(pw, salt)
if err != nil {
return nil, err
}
cipher, err := miscreant.NewAEAD(aeadAlgo, dk, 0)
if err != nil {
return nil, err
}
return cipher.Seal(salt, nil, key, nil), nil
}
func genRandomSalt() []byte {
var b [saltLen]byte
if _, err := io.ReadFull(rand.Reader, b[:]); err != nil {
panic(err)
}
return b[:]
}
package userenckey
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"encoding/pem"
)
func encodePublicKeyToPEM(pub *ecdsa.PublicKey) ([]byte, error) {
der, err := x509.MarshalPKIXPublicKey(pub)
if err != nil {
return nil, err
}
return pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: der}), nil
}
// GenerateKey generates a new ECDSA key pair, and returns the
// PEM-encoded public and private key (in order).
func GenerateKey() ([]byte, []byte, error) {
pkey, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
if err != nil {
return nil, nil, err
}
privBytes, err := encodePrivateKeyToPEM(pkey)
if err != nil {
return nil, nil, err
}
pubBytes, err := encodePublicKeyToPEM(&pkey.PublicKey)
if err != nil {
return nil, nil, err
}
return pubBytes, privBytes, nil
}
// +build go1.10
package userenckey
import (
"crypto/ecdsa"
"crypto/x509"
"encoding/pem"
)
// Encode a private key to PEM-encoded PKCS8.
func encodePrivateKeyToPEM(priv *ecdsa.PrivateKey) ([]byte, error) {
der, err := x509.MarshalPKCS8PrivateKey(priv)
if err != nil {
return nil, err
}
return pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: der}), nil
}
// +build !go1.10
package userenckey
import (
"bytes"
"crypto/ecdsa"
"crypto/x509"
"encoding/pem"
"os/exec"
)
// Encode a private key to PEM-encoded PKCS8.
//
// In Go versions prior to 1.10, we must shell out to openssl to
// convert the private key to PKCS8 format.
func encodePrivateKeyToPEM(priv *ecdsa.PrivateKey) ([]byte, error) {
der, err := x509.MarshalECPrivateKey(priv)
if err != nil {
return nil, err
}
pkcs1 := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: der})
cmd := exec.Command("/usr/bin/openssl", "pkey")
cmd.Stdin = bytes.NewReader(pkcs1)
return cmd.Output()
}
Copyright (c) 2017 The Miscreant Developers. The canonical list of project
contributors who hold copyright over the project can be found at:
https://github.com/miscreant/miscreant/blob/master/AUTHORS.md
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# miscreant.go [![Build Status][build-shield]][build-link] [![GoDoc][godoc-shield]][godoc-link] [![Go Report Card][goreport-shield]][goreport-link] [![MIT licensed][license-shield]][license-link] [![Gitter Chat][gitter-image]][gitter-link]
[build-shield]: https://secure.travis-ci.org/miscreant/miscreant.svg?branch=master
[build-link]: http://travis-ci.org/miscreant/miscreant
[godoc-shield]: https://godoc.org/github.com/miscreant/miscreant/go?status.svg
[godoc-link]: https://godoc.org/github.com/miscreant/miscreant/go
[goreport-shield]: https://goreportcard.com/badge/github.com/miscreant/miscreant
[goreport-link]: https://goreportcard.com/report/github.com/miscreant/miscreant
[license-shield]: https://img.shields.io/badge/license-MIT-blue.svg
[license-link]: https://github.com/miscreant/miscreant/blob/master/LICENSE.txt
[gitter-image]: https://badges.gitter.im/badge.svg
[gitter-link]: https://gitter.im/miscreant/Lobby
> The best crypto you've never heard of, brought to you by [Phil Rogaway]
Go implementation of **Miscreant**: Advanced symmetric encryption using the
[AES-SIV] ([RFC 5297]) and [CHAIN/STREAM] constructions, providing easy-to-use
(or rather, hard-to-misuse) encryption of individual messages or message
streams.
**AES-SIV** provides [nonce-reuse misuse-resistance] (NRMR): accidentally
reusing a nonce with this construction is not a security catastrophe,
unlike it is with more popular AES encryption modes like [AES-GCM].
With **AES-SIV**, the worst outcome of reusing a nonce is an attacker
can see you've sent the same plaintext twice, as opposed to almost all other
AES modes where it can facilitate [chosen ciphertext attacks] and/or
full plaintext recovery.
For more information, see the [toplevel README.md].
[Phil Rogaway]: https://en.wikipedia.org/wiki/Phillip_Rogaway
[AES-SIV]: https://www.iacr.org/archive/eurocrypt2006/40040377/40040377.pdf
[RFC 5297]: https://tools.ietf.org/html/rfc5297
[CHAIN/STREAM]: http://web.cs.ucdavis.edu/~rogaway/papers/oae.pdf
[nonce-reuse misuse-resistance]: https://www.lvh.io/posts/nonce-misuse-resistance-101.html
[AES-GCM]: https://en.wikipedia.org/wiki/Galois/Counter_Mode
[chosen ciphertext attacks]: https://en.wikipedia.org/wiki/Chosen-ciphertext_attack
[toplevel README.md]: https://github.com/miscreant/miscreant/blob/master/README.md
## Help and Discussion
Have questions? Want to suggest a feature or change?
* [Gitter]: web-based chat about miscreant projects including **miscreant.go**
* [Google Group]: join via web or email ([miscreant-crypto+subscribe@googlegroups.com])
[Gitter]: https://gitter.im/miscreant/Lobby
[Google Group]: https://groups.google.com/forum/#!forum/miscreant-crypto
[miscreant-crypto+subscribe@googlegroups.com]: mailto:miscreant-crypto+subscribe@googlegroups.com?subject=subscribe
## Security Notice
Though this library is written by cryptographic professionals, it has not
undergone a thorough security audit, and cryptographic professionals are still
humans that make mistakes.
This library makes an effort to use constant time operations throughout its
implementation, however actual constant time behavior has not been verified.
Use this library at your own risk.
## API
### Symmetric Encryption (AEAD)
**Miscreant** implements the [cipher.AEAD] interface which provides
[Authenticated Encryption with Associated Data]. This is the main API you
should use for most purposes unless you have highly specific needs that are
covered more specifically by one of the other APIs described below.
#### Creating a cipher instance: `NewAEAD()`
To initialize a `cipher.AEAD`, you will need to select one of the ciphers
below to initialize it with:
* `"AES-SIV"`: CMAC-based construction described in [RFC 5297]. Slower but
standardized and more common.
* `"AES-PMAC-SIV"`: PMAC-based construction. Faster but non-standardized and
only available in the Miscreant libraries.
For performance reasons we recommend **AES-PMAC-SIV** but please be aware it
is only implemented by the Miscreant libraries.
After selecting a cipher, pass in a 32-byte or 64-byte key. Note that these
options are twice the size of what you might be expecting (AES-SIV uses two
AES keys).
You can generate a random key using the `miscreant.generateKey()` method, and
then instantiate a cipher instance by calling `miscreant.newAEAD()`. You will
also need to supply a nonce size. We recommend 16-bytes if you would like to
use random nonces:
```go
// Create a 32-byte AES-SIV key
k := miscreant.GenerateKey(32)
// Create a new cipher.AEAD instance
c := miscreant.newAEAD("AES-PMAC-SIV", k, 16)
```
[cipher.AEAD]: https://golang.org/pkg/crypto/cipher/#AEAD
[Authenticated Encryption with Associated Data]: https://en.wikipedia.org/wiki/Authenticated_encryption
#### Encrypting data: `Seal()`
The `Seal()` method encrypts a message and authenticates a bytestring of
*associated data* under a given key and nonce.
The `miscreant.GenerateNonce()` function can be used to randomly generate a
nonce for the message to be encrypted under. If you wish to use this approach
(alternatively you can use a counter for the nonce), please make sure to pass
a `nonceSize` of 16-bytes or greater to `newAEAD()`.
Example:
```go
import "github.com/miscreant/miscreant/go"
// Create a 32-byte AES-SIV key
k := miscreant.GenerateKey(32)
// Create a new cipher.AEAD instance with PMAC as the MAC
c := miscreant.NewAEAD("AES-PMAC-SIV", k, 16)
// Plaintext to be encrypted
pt := []byte("Hello, world!")
// Nonce to encrypt it under
n := miscreant.GenerateNonce(c)
// Associated data to authenticate along with the message
// (or nil if we don't care)
ad := nil
// Create a destination buffer to hold the ciphertext. We need it to be the
// length of the plaintext plus `c.Overhead()` to hold the IV/tag
ct := make([]byte, len(pt) + c.Overhead())
// Perform encryption by calling 'Seal'. The encrypted ciphertext will be
// written into the `ct` buffer
c.Seal(ct, n, pt, ad)
```
#### Decryption (#open)
The `Open()` method decrypts a ciphertext with the given key.
Example:
```go
import "github.com/miscreant/miscreant/go"
// Load an existing cryptographic key
k := ...
// Create a new cipher.AEAD instance
c := miscreant.newAEAD("AES-PMAC-SIV", k, 16)
// Ciphertext to be decrypted
ct := ...
// Nonce under which the ciphertext was originally encrypted
n := ...
// Associated data to authenticate along with the message
// (or nil if we don't care)
ad := nil
// Create a destination buffer to hold the resulting plaintext.
// We need it to be the length of the ciphertext less `c.Overhead()`
l := len(ct) - c.Overhead()
if l < 0 {
panic("ciphertext too short!")
}
pt := make([]byte, l)
// Perform decryption by calling 'Open'. The decrypted plaintext will be
// written into the `pt` buffer
_, err := c.Open(pt, n, ct, ad)
if err != nil {
panic(err)
}
```
## Code of Conduct
We abide by the [Contributor Covenant][cc] and ask that you do as well.
For more information, please see [CODE_OF_CONDUCT.md].
[cc]: https://contributor-covenant.org
[CODE_OF_CONDUCT.md]: https://github.com/miscreant/miscreant/blob/master/CODE_OF_CONDUCT.md
## Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/miscreant/miscreant
## Copyright
Copyright (c) 2017 [The Miscreant Developers][AUTHORS].
See [LICENSE.txt] for further details.
[AUTHORS]: https://github.com/miscreant/miscreant/blob/master/AUTHORS.md
[LICENSE.txt]: https://github.com/miscreant/miscreant/blob/master/LICENSE.txt
// Written in 2015 by Dmitry Chestnykh.
package miscreant
import (
"crypto/cipher"
"crypto/rand"
"io"
)
// Minimum nonce size for which we'll allow the generation of random nonces
const minimumRandomNonceSize = 16
// aead is a wrapper for Cipher implementing cipher.AEAD interface.
type aead struct {
// miscreant.Cipher instance underlying this AEAD
c *Cipher
// Size of the nonce required
nonceSize int
}
// GenerateKey generates a random 32-byte or 64-byte encryption key.
// Panics if the key size is unsupported or source of randomness fails.
func GenerateKey(length int) []byte {
if length != 32 && length != 64 {
panic("miscreant.GenerateKey: invalid key size: " + string(length))
}
key := make([]byte, length)
_, err := io.ReadFull(rand.Reader, key[:])
if err != nil {
panic(err)
}
return key
}
// GenerateNonce generates a random nonce for the given `cipher.AEAD`.
// Panics if the configured nonce size is less than 16-bytes (128-bits)
func GenerateNonce(c cipher.AEAD) []byte {
if c.NonceSize() < minimumRandomNonceSize {
panic("miscreant.GenerateNonce: nonce size is too small: " + string(c.NonceSize()))
}
nonce := make([]byte, c.NonceSize())
_, err := io.ReadFull(rand.Reader, nonce[:])
if err != nil {
panic(err)
}
return nonce
}
// NewAEAD returns an AES-SIV instance implementing cipher.AEAD interface,
// with the given cipher, nonce size, and a key which must be twice as long
// as an AES key, either 32 or 64 bytes to select AES-128 (AES-SIV-256)
// or AES-256 (AES-SIV-512).
//
// Unless the given nonce size is less than zero, Seal and Open will panic when
// passed nonce of a different size.
func NewAEAD(alg string, key []byte, nonceSize int) (cipher.AEAD, error) {
switch alg {
case "AES-SIV", "AES-CMAC-SIV":
c, err := NewAESCMACSIV(key)
if err != nil {
return nil, err
}
return &aead{c: c, nonceSize: nonceSize}, nil
case "AES-PMAC-SIV":
c, err := NewAESPMACSIV(key)
if err != nil {
return nil, err
}
return &aead{c: c, nonceSize: nonceSize}, nil
default:
panic("NewAEAD: unknown cipher: " + alg)
}
}
func (a *aead) NonceSize() int { return a.nonceSize }
func (a *aead) Overhead() int { return a.c.Overhead() }
func (a *aead) Seal(dst, nonce, plaintext, data []byte) (out []byte) {
if len(nonce) != a.nonceSize && a.nonceSize >= 0 {
panic("siv.AEAD: incorrect nonce length")
}
var err error
if data == nil {
out, err = a.c.Seal(dst, plaintext, nonce)
} else {
out, err = a.c.Seal(dst, plaintext, data, nonce)
}
if err != nil {
panic("siv.AEAD: " + err.Error())
}
return out
}
func (a *aead) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
if len(nonce) != a.nonceSize && a.nonceSize >= 0 {
panic("siv.AEAD: incorrect nonce length")
}
if data == nil {
return a.c.Open(dst, ciphertext, nonce)
}
return a.c.Open(dst, ciphertext, data, nonce)
}
// Common block cipher functionality shared across this library
package block
import (
"crypto/cipher"
"crypto/subtle"
)
const (
// Size of an AES block in bytes
Size = 16
// R is the minimal irreducible polynomial for a 128-bit block size
R = 0x87
)
// Block is a 128-byte array used by certain block ciphers (i.e. AES)
type Block [Size]byte
// Clear zeroes out the contents of the block
func (b *Block) Clear() {
// TODO: use a more secure zeroing method that won't be optimized away
for i := range b {
b[i] = 0
}
}
// Dbl performs a doubling of a block over GF(2^128):
//
// a<<1 if firstbit(a)=0
// (a<<1) ⊕ 0¹²⁰10000111 if firstbit(a)=1
//
func (b *Block) Dbl() {
var z byte
for i := Size - 1; i >= 0; i-- {
zz := b[i] >> 7
b[i] = b[i]<<1 | z
z = zz
}
b[Size-1] ^= byte(subtle.ConstantTimeSelect(int(z), R, 0))
}
// Encrypt a block with the given block cipher
func (b *Block) Encrypt(c cipher.Block) {
c.Encrypt(b[:], b[:])
}
#!/bin/bash
set -e
go get -u github.com/golang/lint/golint
go get -u github.com/kisielk/errcheck
go vet ./...
go test -v ./...
golint -set_exit_status ./...
errcheck ./...
\ No newline at end of file
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// CMAC message authentication code, defined in
// NIST Special Publication SP 800-38B.
package cmac