github.com/trustbloc/kms-go@v1.1.2/secretlock/local/masterlock/pbkdf2/master_secret_lock.go (about) 1 /* 2 Copyright SecureKey Technologies Inc. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package pbkdf2 8 9 import ( 10 "crypto/cipher" 11 "crypto/sha256" 12 "encoding/base64" 13 "fmt" 14 "hash" 15 16 "github.com/google/tink/go/subtle/random" 17 "golang.org/x/crypto/pbkdf2" 18 19 "github.com/trustbloc/kms-go/spi/secretlock" 20 21 cipherutil "github.com/trustbloc/kms-go/secretlock/local/internal/cipher" 22 ) 23 24 // package pbkdf2 provides an pbkdf2 implementation of secretlock as a masterlock. 25 // the underlying golang.org/x/crypto/pbkdf2 package implements IETF RFC 8018's PBKDF2 specification found at: 26 // https://tools.ietf.org/html/rfc8018#section-5.2. Similarly the NIST document 800-132 section 5 provides PBKDF 27 // recommendations. 28 29 type masterLockPBKDF2 struct { 30 h func() hash.Hash 31 salt []byte 32 aead cipher.AEAD 33 } 34 35 // NewMasterLock is responsible for encrypting/decrypting with a master key expanded from a passphrase using PBKDF2 36 // using `passphrase`, hash function `h`, `salt`. 37 // The salt is optional and can be set to nil. 38 // This implementation must not be used directly in Aries framework. It should be passed in 39 // as the second argument to local secret lock service constructor: 40 // `local.NewService(masterKeyReader io.Reader, secLock secretlock.Service)`. 41 func NewMasterLock(passphrase string, h func() hash.Hash, iterations int, salt []byte) (secretlock.Service, error) { 42 if passphrase == "" { 43 return nil, fmt.Errorf("passphrase is empty") 44 } 45 46 if h == nil { 47 return nil, fmt.Errorf("hash is nil") 48 } 49 50 size := h().Size() 51 if size > sha256.Size { // AEAD cipher requires at most sha256.Size 52 return nil, fmt.Errorf("hash size not supported") 53 } 54 55 masterKey := pbkdf2.Key([]byte(passphrase), salt, iterations, sha256.Size, h) 56 57 aead, err := cipherutil.CreateAESCipher(masterKey) 58 if err != nil { 59 return nil, err 60 } 61 62 return &masterLockPBKDF2{ 63 h: h, 64 salt: salt, 65 aead: aead, 66 }, nil 67 } 68 69 // Encrypt a master key in req 70 // 71 // (keyURI is used for remote locks, it is ignored by this implementation) 72 func (m *masterLockPBKDF2) Encrypt(keyURI string, req *secretlock.EncryptRequest) (*secretlock.EncryptResponse, error) { 73 nonce := random.GetRandomBytes(uint32(m.aead.NonceSize())) 74 ct := m.aead.Seal(nil, nonce, []byte(req.Plaintext), []byte(req.AdditionalAuthenticatedData)) 75 ct = append(nonce, ct...) 76 77 return &secretlock.EncryptResponse{ 78 Ciphertext: base64.URLEncoding.EncodeToString(ct), 79 }, nil 80 } 81 82 // Decrypt a master key in req 83 // (keyURI is used for remote locks, it is ignored by this implementation). 84 func (m *masterLockPBKDF2) Decrypt(keyURI string, req *secretlock.DecryptRequest) (*secretlock.DecryptResponse, error) { 85 ct, err := base64.URLEncoding.DecodeString(req.Ciphertext) 86 if err != nil { 87 return nil, err 88 } 89 90 nonceSize := uint32(m.aead.NonceSize()) 91 92 // ensure ciphertext contains more than nonce+ciphertext (result from Encrypt()) 93 if len(ct) <= int(nonceSize) { 94 return nil, fmt.Errorf("invalid request") 95 } 96 97 nonce := ct[0:nonceSize] 98 ct = ct[nonceSize:] 99 100 pt, err := m.aead.Open(nil, nonce, ct, []byte(req.AdditionalAuthenticatedData)) 101 if err != nil { 102 return nil, err 103 } 104 105 return &secretlock.DecryptResponse{Plaintext: string(pt)}, nil 106 }