github.com/lbryio/lbcd@v0.22.119/btcec/ciphering.go (about) 1 // Copyright (c) 2015-2016 The btcsuite developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 5 package btcec 6 7 import ( 8 "bytes" 9 "crypto/aes" 10 "crypto/cipher" 11 "crypto/hmac" 12 "crypto/rand" 13 "crypto/sha256" 14 "crypto/sha512" 15 "errors" 16 "io" 17 ) 18 19 var ( 20 // ErrInvalidMAC occurs when Message Authentication Check (MAC) fails 21 // during decryption. This happens because of either invalid private key or 22 // corrupt ciphertext. 23 ErrInvalidMAC = errors.New("invalid mac hash") 24 25 // errInputTooShort occurs when the input ciphertext to the Decrypt 26 // function is less than 134 bytes long. 27 errInputTooShort = errors.New("ciphertext too short") 28 29 // errUnsupportedCurve occurs when the first two bytes of the encrypted 30 // text aren't 0x02CA (= 712 = secp256k1, from OpenSSL). 31 errUnsupportedCurve = errors.New("unsupported curve") 32 33 errInvalidXLength = errors.New("invalid X length, must be 32") 34 errInvalidYLength = errors.New("invalid Y length, must be 32") 35 errInvalidPadding = errors.New("invalid PKCS#7 padding") 36 37 // 0x02CA = 714 38 ciphCurveBytes = [2]byte{0x02, 0xCA} 39 // 0x20 = 32 40 ciphCoordLength = [2]byte{0x00, 0x20} 41 ) 42 43 // GenerateSharedSecret generates a shared secret based on a private key and a 44 // public key using Diffie-Hellman key exchange (ECDH) (RFC 4753). 45 // RFC5903 Section 9 states we should only return x. 46 func GenerateSharedSecret(privkey *PrivateKey, pubkey *PublicKey) []byte { 47 x, _ := pubkey.Curve.ScalarMult(pubkey.X, pubkey.Y, privkey.D.Bytes()) 48 return x.Bytes() 49 } 50 51 // Encrypt encrypts data for the target public key using AES-256-CBC. It also 52 // generates a private key (the pubkey of which is also in the output). The only 53 // supported curve is secp256k1. The `structure' that it encodes everything into 54 // is: 55 // 56 // struct { 57 // // Initialization Vector used for AES-256-CBC 58 // IV [16]byte 59 // // Public Key: curve(2) + len_of_pubkeyX(2) + pubkeyX + 60 // // len_of_pubkeyY(2) + pubkeyY (curve = 714) 61 // PublicKey [70]byte 62 // // Cipher text 63 // Data []byte 64 // // HMAC-SHA-256 Message Authentication Code 65 // HMAC [32]byte 66 // } 67 // 68 // The primary aim is to ensure byte compatibility with Pyelliptic. Also, refer 69 // to section 5.8.1 of ANSI X9.63 for rationale on this format. 70 func Encrypt(pubkey *PublicKey, in []byte) ([]byte, error) { 71 ephemeral, err := NewPrivateKey(S256()) 72 if err != nil { 73 return nil, err 74 } 75 ecdhKey := GenerateSharedSecret(ephemeral, pubkey) 76 derivedKey := sha512.Sum512(ecdhKey) 77 keyE := derivedKey[:32] 78 keyM := derivedKey[32:] 79 80 paddedIn := addPKCSPadding(in) 81 // IV + Curve params/X/Y + padded plaintext/ciphertext + HMAC-256 82 out := make([]byte, aes.BlockSize+70+len(paddedIn)+sha256.Size) 83 iv := out[:aes.BlockSize] 84 if _, err = io.ReadFull(rand.Reader, iv); err != nil { 85 return nil, err 86 } 87 // start writing public key 88 pb := ephemeral.PubKey().SerializeUncompressed() 89 offset := aes.BlockSize 90 91 // curve and X length 92 copy(out[offset:offset+4], append(ciphCurveBytes[:], ciphCoordLength[:]...)) 93 offset += 4 94 // X 95 copy(out[offset:offset+32], pb[1:33]) 96 offset += 32 97 // Y length 98 copy(out[offset:offset+2], ciphCoordLength[:]) 99 offset += 2 100 // Y 101 copy(out[offset:offset+32], pb[33:]) 102 offset += 32 103 104 // start encryption 105 block, err := aes.NewCipher(keyE) 106 if err != nil { 107 return nil, err 108 } 109 mode := cipher.NewCBCEncrypter(block, iv) 110 mode.CryptBlocks(out[offset:len(out)-sha256.Size], paddedIn) 111 112 // start HMAC-SHA-256 113 hm := hmac.New(sha256.New, keyM) 114 hm.Write(out[:len(out)-sha256.Size]) // everything is hashed 115 copy(out[len(out)-sha256.Size:], hm.Sum(nil)) // write checksum 116 117 return out, nil 118 } 119 120 // Decrypt decrypts data that was encrypted using the Encrypt function. 121 func Decrypt(priv *PrivateKey, in []byte) ([]byte, error) { 122 // IV + Curve params/X/Y + 1 block + HMAC-256 123 if len(in) < aes.BlockSize+70+aes.BlockSize+sha256.Size { 124 return nil, errInputTooShort 125 } 126 127 // read iv 128 iv := in[:aes.BlockSize] 129 offset := aes.BlockSize 130 131 // start reading pubkey 132 if !bytes.Equal(in[offset:offset+2], ciphCurveBytes[:]) { 133 return nil, errUnsupportedCurve 134 } 135 offset += 2 136 137 if !bytes.Equal(in[offset:offset+2], ciphCoordLength[:]) { 138 return nil, errInvalidXLength 139 } 140 offset += 2 141 142 xBytes := in[offset : offset+32] 143 offset += 32 144 145 if !bytes.Equal(in[offset:offset+2], ciphCoordLength[:]) { 146 return nil, errInvalidYLength 147 } 148 offset += 2 149 150 yBytes := in[offset : offset+32] 151 offset += 32 152 153 pb := make([]byte, 65) 154 pb[0] = byte(0x04) // uncompressed 155 copy(pb[1:33], xBytes) 156 copy(pb[33:], yBytes) 157 // check if (X, Y) lies on the curve and create a Pubkey if it does 158 pubkey, err := ParsePubKey(pb, S256()) 159 if err != nil { 160 return nil, err 161 } 162 163 // check for cipher text length 164 if (len(in)-aes.BlockSize-offset-sha256.Size)%aes.BlockSize != 0 { 165 return nil, errInvalidPadding // not padded to 16 bytes 166 } 167 168 // read hmac 169 messageMAC := in[len(in)-sha256.Size:] 170 171 // generate shared secret 172 ecdhKey := GenerateSharedSecret(priv, pubkey) 173 derivedKey := sha512.Sum512(ecdhKey) 174 keyE := derivedKey[:32] 175 keyM := derivedKey[32:] 176 177 // verify mac 178 hm := hmac.New(sha256.New, keyM) 179 hm.Write(in[:len(in)-sha256.Size]) // everything is hashed 180 expectedMAC := hm.Sum(nil) 181 if !hmac.Equal(messageMAC, expectedMAC) { 182 return nil, ErrInvalidMAC 183 } 184 185 // start decryption 186 block, err := aes.NewCipher(keyE) 187 if err != nil { 188 return nil, err 189 } 190 mode := cipher.NewCBCDecrypter(block, iv) 191 // same length as ciphertext 192 plaintext := make([]byte, len(in)-offset-sha256.Size) 193 mode.CryptBlocks(plaintext, in[offset:len(in)-sha256.Size]) 194 195 return removePKCSPadding(plaintext) 196 } 197 198 // Implement PKCS#7 padding with block size of 16 (AES block size). 199 200 // addPKCSPadding adds padding to a block of data 201 func addPKCSPadding(src []byte) []byte { 202 padding := aes.BlockSize - len(src)%aes.BlockSize 203 padtext := bytes.Repeat([]byte{byte(padding)}, padding) 204 return append(src, padtext...) 205 } 206 207 // removePKCSPadding removes padding from data that was added with addPKCSPadding 208 func removePKCSPadding(src []byte) ([]byte, error) { 209 length := len(src) 210 padLength := int(src[length-1]) 211 if padLength > aes.BlockSize || length < aes.BlockSize { 212 return nil, errInvalidPadding 213 } 214 215 return src[:length-padLength], nil 216 }