github.com/trustbloc/kms-go@v1.1.2/crypto/tinkcrypto/crypto.go (about) 1 /* 2 Copyright SecureKey Technologies Inc. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 // Package tinkcrypto provides the default implementation of the 8 // common pkg/common/api/crypto.Crypto interface and the SPI pkg/framework/aries.crypto interface 9 // 10 // It uses github.com/tink/go crypto primitives 11 package tinkcrypto 12 13 import ( 14 "errors" 15 "fmt" 16 17 "github.com/google/tink/go/aead" 18 aeadsubtle "github.com/google/tink/go/aead/subtle" 19 "github.com/google/tink/go/core/primitiveset" 20 "github.com/google/tink/go/keyset" 21 "github.com/google/tink/go/mac" 22 "github.com/google/tink/go/signature" 23 "golang.org/x/crypto/chacha20poly1305" 24 25 "github.com/trustbloc/kms-go/spi/crypto" 26 27 "github.com/trustbloc/kms-go/crypto/tinkcrypto/primitive/aead/subtle" 28 "github.com/trustbloc/kms-go/crypto/tinkcrypto/primitive/bbs" 29 ) 30 31 const ( 32 // ECDHESA256KWAlg is the ECDH-ES with AES-GCM 256 key wrapping algorithm. 33 ECDHESA256KWAlg = "ECDH-ES+A256KW" 34 // ECDH1PUA128KWAlg is the ECDH-1PU with AES-CBC 128+HMAC-SHA 256 key wrapping algorithm. 35 ECDH1PUA128KWAlg = "ECDH-1PU+A128KW" 36 // ECDH1PUA192KWAlg is the ECDH-1PU with AES-CBC 192+HMAC-SHA 384 key wrapping algorithm. 37 ECDH1PUA192KWAlg = "ECDH-1PU+A192KW" 38 // ECDH1PUA256KWAlg is the ECDH-1PU with AES-CBC 256+HMAC-SHA 512 key wrapping algorithm. 39 ECDH1PUA256KWAlg = "ECDH-1PU+A256KW" 40 // ECDHESXC20PKWAlg is the ECDH-ES with XChacha20Poly1305 key wrapping algorithm. 41 ECDHESXC20PKWAlg = "ECDH-ES+XC20PKW" 42 // ECDH1PUXC20PKWAlg is the ECDH-1PU with XChacha20Poly1305 key wrapping algorithm. 43 ECDH1PUXC20PKWAlg = "ECDH-1PU+XC20PKW" 44 45 nistPECDHKWPrivateKeyTypeURL = "type.hyperledger.org/hyperledger.aries.crypto.tink.NistPEcdhKwPrivateKey" 46 x25519ECDHKWPrivateKeyTypeURL = "type.hyperledger.org/hyperledger.aries.crypto.tink.X25519EcdhKwPrivateKey" 47 ) 48 49 var errBadKeyHandleFormat = errors.New("bad key handle format") 50 51 // Package tinkcrypto includes the default implementation of pkg/crypto. It uses Tink for executing crypto primitives 52 // and will be built as a framework option. It represents the main crypto service in the framework. `kh interface{}` 53 // arguments in this implementation represent Tink's `*keyset.Handle`, using this type provides easy integration with 54 // Tink and the default KMS service. 55 56 // Crypto is the default Crypto SPI implementation using Tink. 57 type Crypto struct { 58 ecKW keyWrapper 59 okpKW keyWrapper 60 } 61 62 // New creates a new Crypto instance. 63 func New() (*Crypto, error) { 64 return &Crypto{ecKW: &ecKWSupport{}, okpKW: &okpKWSupport{}}, nil 65 } 66 67 // Encrypt will encrypt msg using the implementation's corresponding encryption key and primitive in kh of a public key. 68 func (t *Crypto) Encrypt(msg, aad []byte, kh interface{}) ([]byte, []byte, error) { 69 keyHandle, ok := kh.(*keyset.Handle) 70 if !ok { 71 return nil, nil, errBadKeyHandleFormat 72 } 73 74 ps, err := keyHandle.Primitives() 75 if err != nil { 76 return nil, nil, fmt.Errorf("get primitives: %w", err) 77 } 78 79 a, err := aead.New(keyHandle) 80 if err != nil { 81 return nil, nil, fmt.Errorf("create new aead: %w", err) 82 } 83 84 ct, err := a.Encrypt(msg, aad) 85 if err != nil { 86 return nil, nil, fmt.Errorf("encrypt msg: %w", err) 87 } 88 89 // Tink appends a key prefix + nonce to ciphertext, let's remove them to get the raw ciphertext 90 ivSize := nonceSize(ps) 91 prefixLength := len(ps.Primary.Prefix) 92 cipherText := ct[prefixLength+ivSize:] 93 nonce := ct[prefixLength : prefixLength+ivSize] 94 95 return cipherText, nonce, nil 96 } 97 98 func nonceSize(ps *primitiveset.PrimitiveSet) int { 99 var ivSize int 100 // AESGCM and XChacha20Poly1305 nonce sizes supported only for now 101 switch ps.Primary.Primitive.(type) { 102 case *aeadsubtle.XChaCha20Poly1305: 103 ivSize = chacha20poly1305.NonceSizeX 104 case *aeadsubtle.AESGCM: 105 ivSize = aeadsubtle.AESGCMIVSize 106 case *aeadsubtle.EncryptThenAuthenticate: 107 // AESCBC+HMACSHA Tink keys use Tink's EncryptThenAuthenticate AEAD primitive as per the CBC hmac key manager's 108 // Primitive() call. 109 ivSize = subtle.AES128Size 110 default: 111 ivSize = aeadsubtle.AESGCMIVSize 112 } 113 114 return ivSize 115 } 116 117 // Decrypt will decrypt cipher using the implementation's corresponding encryption key referenced by kh of 118 // a private key. 119 func (t *Crypto) Decrypt(cipher, aad, nonce []byte, kh interface{}) ([]byte, error) { 120 keyHandle, ok := kh.(*keyset.Handle) 121 if !ok { 122 return nil, errBadKeyHandleFormat 123 } 124 125 ps, err := keyHandle.Primitives() 126 if err != nil { 127 return nil, fmt.Errorf("get primitives: %w", err) 128 } 129 130 a, err := aead.New(keyHandle) 131 if err != nil { 132 return nil, fmt.Errorf("create new aead: %w", err) 133 } 134 135 for prefix := range ps.Entries { 136 // since Tink expects the key prefix + nonce as the ciphertext prefix, prepend them prior to calling its Decrypt() 137 ct := make([]byte, 0, len(prefix)+len(nonce)+len(cipher)) 138 ct = append(ct, prefix...) 139 ct = append(ct, nonce...) 140 ct = append(ct, cipher...) 141 142 pt, e := a.Decrypt(ct, aad) 143 144 if e == nil { 145 return pt, nil 146 } 147 } 148 149 return nil, fmt.Errorf("decrypt cipher: decryption failed") 150 } 151 152 // Sign will sign msg using the implementation's corresponding signing key referenced by kh of a private key. 153 func (t *Crypto) Sign(msg []byte, kh interface{}) ([]byte, error) { 154 keyHandle, ok := kh.(*keyset.Handle) 155 if !ok { 156 return nil, errBadKeyHandleFormat 157 } 158 159 signer, err := signature.NewSigner(keyHandle) 160 if err != nil { 161 return nil, fmt.Errorf("create new signer: %w", err) 162 } 163 164 s, err := signer.Sign(msg) 165 if err != nil { 166 return nil, fmt.Errorf("sign msg: %w", err) 167 } 168 169 return s, nil 170 } 171 172 // Verify will verify sig signature of msg using the implementation's corresponding signing key referenced by kh of 173 // a public key. 174 func (t *Crypto) Verify(sig, msg []byte, kh interface{}) error { 175 keyHandle, ok := kh.(*keyset.Handle) 176 if !ok { 177 return errBadKeyHandleFormat 178 } 179 180 verifier, err := signature.NewVerifier(keyHandle) 181 if err != nil { 182 return fmt.Errorf("create new verifier: %w", err) 183 } 184 185 err = verifier.Verify(sig, msg) 186 if err != nil { 187 err = fmt.Errorf("verify msg: %w", err) 188 } 189 190 return err 191 } 192 193 // ComputeMAC computes message authentication code (MAC) for code data 194 // using a matching MAC primitive in kh key handle. 195 func (t *Crypto) ComputeMAC(data []byte, kh interface{}) ([]byte, error) { 196 keyHandle, ok := kh.(*keyset.Handle) 197 if !ok { 198 return nil, errBadKeyHandleFormat 199 } 200 201 macPrimitive, err := mac.New(keyHandle) 202 if err != nil { 203 return nil, err 204 } 205 206 return macPrimitive.ComputeMAC(data) 207 } 208 209 // VerifyMAC determines if mac is a correct authentication code (MAC) for data 210 // using a matching MAC primitive in kh key handle and returns nil if so, otherwise it returns an error. 211 func (t *Crypto) VerifyMAC(macBytes, data []byte, kh interface{}) error { 212 keyHandle, ok := kh.(*keyset.Handle) 213 if !ok { 214 return errBadKeyHandleFormat 215 } 216 217 macPrimitive, err := mac.New(keyHandle) 218 if err != nil { 219 return err 220 } 221 222 return macPrimitive.VerifyMAC(macBytes, data) 223 } 224 225 // WrapKey will do ECDH (ES or 1PU) key wrapping of cek using apu, apv and recipient public key 'recPubKey'. 226 // This function is used with the following parameters: 227 // - Key Wrapping: `ECDH-ES` (no options) or `ECDH-1PU` (using crypto.WithSender() option in wrapKeyOpts) over either: 228 // - `ECDH-ES+A256KW` alg (AES256-GCM, default anoncrypt KW with no options) as per 229 // https://tools.ietf.org/html/rfc7518#appendix-A.2 230 // - `ECDH-ES+XC20PKW` alg (XChacha20Poly1305, anoncrypt using crypto.WithXC20PKW() option in wrapKeyOpts). 231 // The following ECDH-1PU algs are triggered using the crypto.WithSender() and crypto.WithTag() options in 232 // wrapKeyOpts: 233 // - `ECDH-1PU+A128KW` alg (AES128-GCM, authcrypt KW using cek size=32). 234 // - `ECDH-1PU+A192KW` alg (AES192-GCM, authcrypt KW using cek size=48). 235 // - `ECDH-1PU+A256KW` alg (AES256-GCM, authcrypt KW using cek size=64). 236 // - `ECDH-1PU+XC20PKW` alg (XChacha20Poly1305, authcrypt using crypto.WithXC20PKW() with cek size=32). 237 // - KDF (based on recPubKey.Curve): 238 // `Concat KDF` as per https://tools.ietf.org/html/rfc7518#section-4.6 (for recPubKey with NIST P curves) or 239 // `Curve25519`+`Concat KDF` as per https://tools.ietf.org/html/rfc7748#section-6.1 240 // (for recPubKey with X25519 curve). 241 // 242 // returns the resulting key wrapping info as *composite.RecipientWrappedKey or error in case of wrapping failure. 243 func (t *Crypto) WrapKey(cek, apu, apv []byte, recPubKey *crypto.PublicKey, 244 wrapKeyOpts ...crypto.WrapKeyOpts) (*crypto.RecipientWrappedKey, error) { 245 if recPubKey == nil { 246 return nil, errors.New("wrapKey: recipient public key is required") 247 } 248 249 pOpts := crypto.NewOpt() 250 251 for _, opt := range wrapKeyOpts { 252 opt(pOpts) 253 } 254 255 wk, err := t.deriveKEKAndWrap(cek, apu, apv, pOpts.Tag(), pOpts.SenderKey(), recPubKey, pOpts.EPK(), 256 pOpts.UseXC20PKW()) 257 if err != nil { 258 return nil, fmt.Errorf("wrapKey: %w", err) 259 } 260 261 return wk, nil 262 } 263 264 // UnwrapKey unwraps a key in recWK using ECDH (ES or 1PU) with recipient private key kh. 265 // This function is used with the following parameters: 266 // - Key Unwrapping: `ECDH-ES` (no options) or `ECDH-1PU` (using crypto.WithSender() option in wrapKeyOpts) 267 // over either 268 // - `ECDH-ES+A256KW` alg (AES256-GCM, default anoncrypt KW with no options) as per 269 // https://tools.ietf.org/html/rfc7518#appendix-A.2 270 // - `ECDH-ES+XC20PKW` alg (XChacha20Poly1305, anoncrypt using crypto.WithXC20PKW() option in wrapKeyOpts). 271 // The following ECDH-1PU algs are triggered using the crypto.WithSender() and crypto.WithTag() options in 272 // wrapKeyOpts: 273 // - `ECDH-1PU+A128KW` alg (AES128-GCM, authcrypt KW using cek size=32). 274 // - `ECDH-1PU+A192KW` alg (AES192-GCM, authcrypt KW using cek size=48). 275 // - `ECDH-1PU+A256KW` alg (AES256-GCM, authcrypt KW using cek size=64). 276 // - `ECDH-1PU+XC20PKW` alg (XChacha20Poly1305, authcrypt using crypto.WithXC20PKW() with cek size=32). 277 // - KDF (based on recWk.EPK.KeyType): `Concat KDF` as per https://tools.ietf.org/html/rfc7518#section-4.6 (for type 278 // value as EC) or `Curve25519`+`Concat KDF` as per https://tools.ietf.org/html/rfc7748#section-6.1 (for type value 279 // as OKP, ie X25519 key). 280 // 281 // returns the resulting unwrapping key or error in case of unwrapping failure. 282 // 283 // Notes: 284 // 1- if the crypto.WithSender() option was used in WrapKey(), then it must be set here as well for successful key 285 // 286 // unwrapping. 287 // 288 // 2- unwrapping a key with recWK.alg value set as either `ECDH-1PU+A128KW`, `ECDH-1PU+A192KW`, `ECDH-1PU+A256KW` or 289 // 290 // `ECDH-1PU+XC20PKW` requires the use of crypto.WithSender() option (containing the sender public key) in order to 291 // execute ECDH-1PU derivation. 292 // 293 // 3- the ephemeral key in recWK.EPK must have the same KeyType as the recipientKH and the same Curve for NIST P 294 // 295 // curved keys. Unwrapping a key with non matching types/curves will result in unwrapping failure. 296 // 297 // 4- recipientKH must contain the private key since unwrapping is usually done on the recipient side. 298 func (t *Crypto) UnwrapKey(recWK *crypto.RecipientWrappedKey, recipientKH interface{}, 299 wrapKeyOpts ...crypto.WrapKeyOpts) ([]byte, error) { 300 if recWK == nil { 301 return nil, fmt.Errorf("unwrapKey: RecipientWrappedKey is empty") 302 } 303 304 pOpts := crypto.NewOpt() 305 306 for _, opt := range wrapKeyOpts { 307 opt(pOpts) 308 } 309 310 key, err := t.deriveKEKAndUnwrap(recWK.Alg, recWK.EncryptedCEK, recWK.APU, recWK.APV, pOpts.Tag(), &recWK.EPK, 311 pOpts.SenderKey(), recipientKH) 312 if err != nil { 313 return nil, fmt.Errorf("unwrapKey: %w", err) 314 } 315 316 return key, nil 317 } 318 319 // SignMulti will create a BBS+ signature of messages using the signer's private key in signerKH handle. 320 // returns: 321 // 322 // signature in []byte 323 // error in case of errors 324 func (t *Crypto) SignMulti(messages [][]byte, signerKH interface{}) ([]byte, error) { 325 keyHandle, ok := signerKH.(*keyset.Handle) 326 if !ok { 327 return nil, errBadKeyHandleFormat 328 } 329 330 signer, err := bbs.NewSigner(keyHandle) 331 if err != nil { 332 return nil, fmt.Errorf("create new BBS+ signer: %w", err) 333 } 334 335 s, err := signer.Sign(messages) 336 if err != nil { 337 return nil, fmt.Errorf("BBS+ sign msg: %w", err) 338 } 339 340 return s, nil 341 } 342 343 // VerifyMulti will BBS+ verify a signature of messages against the signer's public key in signerPubKH handle. 344 // returns: 345 // 346 // error in case of errors or nil if signature verification was successful 347 func (t *Crypto) VerifyMulti(messages [][]byte, bbsSignature []byte, signerPubKH interface{}) error { 348 keyHandle, ok := signerPubKH.(*keyset.Handle) 349 if !ok { 350 return errBadKeyHandleFormat 351 } 352 353 verifier, err := bbs.NewVerifier(keyHandle) 354 if err != nil { 355 return fmt.Errorf("create new BBS+ verifier: %w", err) 356 } 357 358 err = verifier.Verify(messages, bbsSignature) 359 if err != nil { 360 err = fmt.Errorf("BBS+ verify msg: %w", err) 361 } 362 363 return err 364 } 365 366 // VerifyProof will verify a BBS+ signature proof (generated e.g. by Verifier's DeriveProof() call) for revealedMessages 367 // with the signer's public key in signerPubKH handle. 368 // returns: 369 // 370 // error in case of errors or nil if signature proof verification was successful 371 func (t *Crypto) VerifyProof(revealedMessages [][]byte, proof, nonce []byte, signerPubKH interface{}) error { 372 keyHandle, ok := signerPubKH.(*keyset.Handle) 373 if !ok { 374 return errBadKeyHandleFormat 375 } 376 377 verifier, err := bbs.NewVerifier(keyHandle) 378 if err != nil { 379 return fmt.Errorf("create new BBS+ verifier: %w", err) 380 } 381 382 err = verifier.VerifyProof(revealedMessages, proof, nonce) 383 if err != nil { 384 err = fmt.Errorf("verify proof msg: %w", err) 385 } 386 387 return err 388 } 389 390 // DeriveProof will create a BBS+ signature proof for a list of revealed messages using BBS signature 391 // (can be built using a Signer's SignMulti() call) and the signer's public key in signerPubKH handle. 392 // returns: 393 // 394 // signature proof in []byte 395 // error in case of errors 396 func (t *Crypto) DeriveProof(messages [][]byte, bbsSignature, nonce []byte, revealedIndexes []int, 397 signerPubKH interface{}) ([]byte, error) { 398 keyHandle, ok := signerPubKH.(*keyset.Handle) 399 if !ok { 400 return nil, errBadKeyHandleFormat 401 } 402 403 verifier, err := bbs.NewVerifier(keyHandle) 404 if err != nil { 405 return nil, fmt.Errorf("create new BBS+ verifier: %w", err) 406 } 407 408 proof, err := verifier.DeriveProof(messages, bbsSignature, nonce, revealedIndexes) 409 if err != nil { 410 return nil, fmt.Errorf("verify proof msg: %w", err) 411 } 412 413 return proof, nil 414 }