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