github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/stellar/bundle/boxer_test.go (about) 1 package bundle 2 3 import ( 4 "context" 5 "crypto/sha256" 6 "encoding/base64" 7 "errors" 8 "os" 9 "testing" 10 11 "github.com/davecgh/go-spew/spew" 12 "github.com/keybase/client/go/libkb" 13 "github.com/keybase/client/go/protocol/keybase1" 14 "github.com/keybase/client/go/protocol/stellar1" 15 "github.com/stretchr/testify/require" 16 ) 17 18 const v2 = stellar1.BundleVersion_V2 19 20 func testBundle(t *testing.T) stellar1.Bundle { 21 secretKey := stellar1.SecretKey("SDGCPMBQHYAIWM3PQOEKWICDMLVT7REJ24J26QEYJYGB6FJRPTKDULQX") 22 newBundle, err := New(secretKey, "test") 23 require.NoError(t, err) 24 newBundle.Accounts[0].IsPrimary = true 25 return *newBundle 26 } 27 28 func TestBundleRoundtrip(t *testing.T) { 29 m := libkb.NewMetaContext(context.Background(), nil) 30 31 ring := newPukRing() 32 pukSeed, pukGen := ring.makeGen(t, 1) 33 bundle := testBundle(t) 34 t.Logf("puk seed (hex): %v", toB64(pukSeed[:])) 35 t.Logf("puk gen: %v", pukGen) 36 37 boxed, err := BoxAndEncode(&bundle, pukGen, pukSeed) 38 require.NoError(t, err) 39 t.Logf("outer enc b64: %v", boxed.EncParentB64) 40 t.Logf("outer vis b64: %v", boxed.VisParentB64) 41 t.Logf("enc.N b64: %v", toB64(boxed.EncParent.N[:])) 42 t.Logf("enc.E b64: %v", toB64(boxed.EncParent.E)) 43 require.Equal(t, v2, boxed.FormatVersionParent) 44 require.NotEmpty(t, boxed.VisParentB64) 45 require.NotEmpty(t, boxed.EncParentB64) 46 require.Len(t, boxed.AcctBundles, 1) 47 require.True(t, len(boxed.EncParentB64) > 100) 48 require.NotZero(t, boxed.EncParent.N) 49 require.Equal(t, 2, boxed.EncParent.V) 50 require.True(t, len(boxed.EncParent.E) > 100) 51 require.Equal(t, pukGen, boxed.EncParent.Gen) 52 53 bundle2, version, decodedPukGen, accountGens, err := DecodeAndUnbox(m, ring, boxed.toBundleEncodedB64()) 54 require.NoError(t, err) 55 require.Equal(t, v2, version) 56 require.Equal(t, pukGen, decodedPukGen) 57 require.Nil(t, bundle2.Prev) 58 require.NotNil(t, bundle2.OwnHash) 59 require.Equal(t, bundle.Revision, bundle2.Revision) 60 require.Equal(t, len(bundle.Accounts), len(bundle2.Accounts)) 61 for i, acct := range bundle.Accounts { 62 acct2 := bundle2.Accounts[i] 63 require.Equal(t, acct.AccountID, acct2.AccountID) 64 require.Equal(t, acct.Mode, acct2.Mode) 65 require.Equal(t, acct.Name, acct2.Name) 66 require.Equal(t, acct.IsPrimary, acct2.IsPrimary) 67 require.Equal(t, acct.AcctBundleRevision, acct2.AcctBundleRevision) 68 signers1 := bundle.AccountBundles[acct.AccountID].Signers 69 signers2 := bundle2.AccountBundles[acct2.AccountID].Signers 70 require.Equal(t, signers1, signers2) 71 require.True(t, len(signers2) == 1) // exactly one signer 72 require.True(t, len(signers2[0]) > 0) 73 require.Equal(t, keybase1.PerUserKeyGeneration(1), accountGens[acct.AccountID]) 74 } 75 } 76 77 func TestBundlePrevs(t *testing.T) { 78 m := libkb.NewMetaContext(context.Background(), nil) 79 ring := newPukRing() 80 pukSeed, pukGen := ring.makeGen(t, 1) 81 b1 := testBundle(t) 82 83 // encode and decode b1 to populate OwnHash 84 b1Boxed, err := BoxAndEncode(&b1, pukGen, pukSeed) 85 require.NoError(t, err) 86 b1Decoded, _, _, _, err := DecodeAndUnbox(m, ring, b1Boxed.toBundleEncodedB64()) 87 require.NoError(t, err) 88 89 // make a change, and verify hashes are correct 90 b2 := b1Decoded.DeepCopy() 91 b2.Accounts[0].Name = "apples" 92 b2.Prev = b1Decoded.OwnHash 93 b2.OwnHash = nil 94 b2.Revision++ 95 b2Boxed, err := BoxAndEncode(&b2, pukGen, pukSeed) 96 require.NoError(t, err) 97 b2Decoded, _, _, _, err := DecodeAndUnbox(m, ring, b2Boxed.toBundleEncodedB64()) 98 require.NoError(t, err) 99 require.Equal(t, "apples", b2Decoded.Accounts[0].Name, "change carried thru") 100 require.NotNil(t, b2Decoded.Prev) 101 require.Equal(t, b2Decoded.Prev, b1Decoded.OwnHash, "b2 prevs to b1") 102 103 // change the keys and do it again 104 pukSeed, pukGen = ring.makeGen(t, 2) 105 b3 := b2Decoded.DeepCopy() 106 b3.Accounts[0].Name = "bananas" 107 b3.Prev = b2Decoded.OwnHash 108 b3.OwnHash = nil 109 b3.Revision++ 110 b3Boxed, err := BoxAndEncode(&b3, pukGen, pukSeed) 111 require.NoError(t, err) 112 b3Decoded, _, bundleGen, accountGens, err := DecodeAndUnbox(m, ring, b3Boxed.toBundleEncodedB64()) 113 require.NoError(t, err) 114 require.Equal(t, "bananas", b3Decoded.Accounts[0].Name, "change carried thru") 115 require.NotNil(t, b3Decoded.Prev) 116 require.Equal(t, b3Decoded.Prev, b2Decoded.OwnHash, "b3 prevs to b2") 117 require.Equal(t, keybase1.PerUserKeyGeneration(2), bundleGen) 118 for _, acct := range b3Decoded.Accounts { 119 require.Equal(t, keybase1.PerUserKeyGeneration(2), accountGens[acct.AccountID]) 120 } 121 } 122 123 func TestBundleRoundtripCorruptionEnc(t *testing.T) { 124 m := libkb.NewMetaContext(context.Background(), nil) 125 bundle := testBundle(t) 126 ring := newPukRing() 127 pukSeed, pukGen := ring.makeGen(t, 4) 128 129 boxed, err := BoxAndEncode(&bundle, pukGen, pukSeed) 130 require.NoError(t, err) 131 replaceWith := "a" 132 if boxed.EncParentB64[85] == 'a' { 133 replaceWith = "b" 134 } 135 boxed.EncParentB64 = boxed.EncParentB64[:85] + replaceWith + boxed.EncParentB64[86:] 136 137 _, _, _, _, err = DecodeAndUnbox(m, ring, boxed.toBundleEncodedB64()) 138 require.Error(t, err) 139 require.Contains(t, err.Error(), "stellar bundle secret box open failed") 140 } 141 142 func TestBundleRoundtripCorruptionVis(t *testing.T) { 143 m := libkb.NewMetaContext(context.Background(), nil) 144 bundle := testBundle(t) 145 ring := newPukRing() 146 pukSeed, pukGen := ring.makeGen(t, 3) 147 148 boxed, err := BoxAndEncode(&bundle, pukGen, pukSeed) 149 require.NoError(t, err) 150 replaceWith := "a" 151 if boxed.VisParentB64[85] == 'a' { 152 replaceWith = "b" 153 } 154 boxed.VisParentB64 = boxed.VisParentB64[:85] + replaceWith + boxed.VisParentB64[86:] 155 156 _, _, _, _, err = DecodeAndUnbox(m, ring, boxed.toBundleEncodedB64()) 157 require.Error(t, err) 158 require.Contains(t, err.Error(), "visible hash mismatch") 159 } 160 161 func TestBoxAndEncodeCatchesMalformedBundles(t *testing.T) { 162 bundle := testBundle(t) 163 ring := newPukRing() 164 pukSeed, pukGen := ring.makeGen(t, 3) 165 166 // put a different account and secret in the AccountBundle 167 newAcctID, newSecret, err := randomStellarKeypair() 168 require.NoError(t, err) 169 newAB := map[stellar1.AccountID]stellar1.AccountBundle{ 170 newAcctID: { 171 AccountID: newAcctID, 172 Signers: []stellar1.SecretKey{newSecret}, 173 }, 174 } 175 bundle.AccountBundles = newAB 176 177 // encode should error because the bundle is invalid 178 _, err = BoxAndEncode(&bundle, pukGen, pukSeed) 179 require.Contains(t, err.Error(), "account in AccountBundles not in Accounts") 180 } 181 182 type accountCan struct { 183 accountID stellar1.AccountID 184 encB64 string 185 } 186 type canned struct { 187 pukSeedB64 string 188 pukGen int 189 encParentB64 string 190 visParentB64 string 191 accounts []accountCan 192 } 193 194 func (c *canned) puk(t *testing.T) (puk libkb.PerUserKeySeed) { 195 bs, err := base64.StdEncoding.DecodeString(c.pukSeedB64) 196 require.NoError(t, err) 197 require.Equal(t, len(puk), len(bs)) 198 copy(puk[:], bs) 199 return puk 200 } 201 202 func (c *canned) gen() keybase1.PerUserKeyGeneration { 203 return keybase1.PerUserKeyGeneration(c.pukGen) 204 } 205 206 func (c *canned) toBundleEncodedB64() BundleEncoded { 207 benc := BundleEncoded{ 208 EncParent: c.encParentB64, 209 VisParent: c.visParentB64, 210 AcctBundles: make(map[stellar1.AccountID]string), 211 } 212 for _, acct := range c.accounts { 213 benc.AcctBundles[acct.accountID] = acct.encB64 214 } 215 return benc 216 } 217 218 func (c *canned) ring(t *testing.T) *pukRing { 219 pukSeed := c.puk(t) 220 pukGen := c.gen() 221 return &pukRing{ 222 map[keybase1.PerUserKeyGeneration]libkb.PerUserKeySeed{ 223 pukGen: pukSeed, 224 }, 225 } 226 } 227 228 var cans = []canned{ 229 // this one is valid 230 {"R81SkpClcSUPMzch6UAstOhS+hbZi4R43HzbRiLQ46o=", 231 3, 232 "hKFlxPCoH32GB0er08rZF1B2sfZNME3/35sFKWBVSCQTFKiTAuGSe2mK9AKCWMLkXcTvoJojtyu56hBwGbXQCqqSL8eY1sb8UGG7SsuvNBr27hzUtosJmb9tCT0uikOY8YFPYAtWbmqHB9QvqeBEtysd7ZDDJPG0cJ9lckvj9rSAE/wuhcVlHMAWfbOvGvOLDf56VVK46Ms7bGTSedTKHj8IPpF48RF1GrDlvZQRgqD8ydwtqMGZ1ZkqF+DKKXaEQaIhY47L50Ynna7Qzm8ZCEujsuo5W3EKtZtY6XG0RYx7AzdhkXKzFVDmINVHxkZbQi66QpWjZ2VuA6FuxBh9I1ef+UMA9u3rOYAqPzeVm6hlam5ZX62hdgI=", 233 "g6hhY2NvdW50c5KFqWFjY291bnRJRNk4R0FXWjdIVlBLUkdDSDJLUDY0NzVYVjZIQTJDQUY0NE1YV1dFNVJLVjRMTU1HQjZGTk5TRVBOUEWyYWNjdEJ1bmRsZVJldmlzaW9uAbFlbmNBY2N0QnVuZGxlSGFzaMQgBgkhqzuIynMJrhIeuSOPTCoS5QutvwDXZr7fHVuCmpipaXNQcmltYXJ5w6Rtb2RlAYWpYWNjb3VudElE2ThHQlBMTEhPS1BSRlNDRTZGUDZEMzdBVVVOWUNKMlRTUE9ITUhBUjU0VEJETFdXUElaQ0Q0UkwzR7JhY2N0QnVuZGxlUmV2aXNpb24BsWVuY0FjY3RCdW5kbGVIYXNoxCCbBuOilM5oKBvC1JaEzJoq8l1W1picV5MxkTEOrPEnpalpc1ByaW1hcnnCpG1vZGUBpHByZXbAqHJldmlzaW9uAQ==", 234 []accountCan{ 235 {"GAWZ7HVPKRGCH2KP6475XV6HA2CAF44MXWWE5RKV4LMMGB6FNNSEPNPE", "hKFlxKUc3fqMOAYv9m6ycpnpQA4CSriSQoPVNbvsXXEgv4WsixkaTThkKGMlYuWvTJdHeqPdYXo/Mw156xq8MaIbzDTriFplFNcLhNYdi8f1ViHqvVIecX2frU/BOtIsAlqknnhl4+Z1u2kUnZYI5pZRvUV5H1loSWC4tBmEoCgK1S6XrLx1POOIiKkH8EFMXVrB6+BjbieW1w8HTXNp0jWbKkq+QoKz8MCjZ2VuA6FuxBibtfMD40wYqnfPsGePn1RphpzcEqv7mZehdgE="}, 236 {"GBPLLHOKPRFSCE6FP6D37AUUNYCJ2TSPOHMHAR54TBDLWWPIZCD4RL3G", "hKFlxKX3zqweGMWyl4vOC8ht0jngDnTEpWqGBePh7okF9S003QStjI9Td1d+urqj/k4etwgLiPO8LHaaQ7o2WAwuSXakHJVJ2xIq+T+MYnoobbx5sArk4wsg7AWTX+uw/rXoyN7P9ZAgDSH+4oZ6/0j8dwR6RAoc9c+dog8xnW3eTkeSlj2KYx6XnEOOlOcCBCKsZePi9eoN92CxvB5MRN6tR7w3PH4yynqjZ2VuA6FuxBha4JmmVyysST3avwMxayHfTExp7tnLwHOhdgE="}, 237 }}, 238 // bad decryption puk 239 {"1111111111111111111111111111111111111111111=", 240 3, 241 "hKFlxPCoH32GB0er08rZF1B2sfZNME3/35sFKWBVSCQTFKiTAuGSe2mK9AKCWMLkXcTvoJojtyu56hBwGbXQCqqSL8eY1sb8UGG7SsuvNBr27hzUtosJmb9tCT0uikOY8YFPYAtWbmqHB9QvqeBEtysd7ZDDJPG0cJ9lckvj9rSAE/wuhcVlHMAWfbOvGvOLDf56VVK46Ms7bGTSedTKHj8IPpF48RF1GrDlvZQRgqD8ydwtqMGZ1ZkqF+DKKXaEQaIhY47L50Ynna7Qzm8ZCEujsuo5W3EKtZtY6XG0RYx7AzdhkXKzFVDmINVHxkZbQi66QpWjZ2VuA6FuxBh9I1ef+UMA9u3rOYAqPzeVm6hlam5ZX62hdgI=", 242 "g6hhY2NvdW50c5KFqWFjY291bnRJRNk4R0FXWjdIVlBLUkdDSDJLUDY0NzVYVjZIQTJDQUY0NE1YV1dFNVJLVjRMTU1HQjZGTk5TRVBOUEWyYWNjdEJ1bmRsZVJldmlzaW9uAbFlbmNBY2N0QnVuZGxlSGFzaMQgBgkhqzuIynMJrhIeuSOPTCoS5QutvwDXZr7fHVuCmpipaXNQcmltYXJ5w6Rtb2RlAYWpYWNjb3VudElE2ThHQlBMTEhPS1BSRlNDRTZGUDZEMzdBVVVOWUNKMlRTUE9ITUhBUjU0VEJETFdXUElaQ0Q0UkwzR7JhY2N0QnVuZGxlUmV2aXNpb24BsWVuY0FjY3RCdW5kbGVIYXNoxCCbBuOilM5oKBvC1JaEzJoq8l1W1picV5MxkTEOrPEnpalpc1ByaW1hcnnCpG1vZGUBpHByZXbAqHJldmlzaW9uAQ==", 243 []accountCan{ 244 {"GAWZ7HVPKRGCH2KP6475XV6HA2CAF44MXWWE5RKV4LMMGB6FNNSEPNPE", "hKFlxKUc3fqMOAYv9m6ycpnpQA4CSriSQoPVNbvsXXEgv4WsixkaTThkKGMlYuWvTJdHeqPdYXo/Mw156xq8MaIbzDTriFplFNcLhNYdi8f1ViHqvVIecX2frU/BOtIsAlqknnhl4+Z1u2kUnZYI5pZRvUV5H1loSWC4tBmEoCgK1S6XrLx1POOIiKkH8EFMXVrB6+BjbieW1w8HTXNp0jWbKkq+QoKz8MCjZ2VuA6FuxBibtfMD40wYqnfPsGePn1RphpzcEqv7mZehdgE="}, 245 {"GBPLLHOKPRFSCE6FP6D37AUUNYCJ2TSPOHMHAR54TBDLWWPIZCD4RL3G", "hKFlxKX3zqweGMWyl4vOC8ht0jngDnTEpWqGBePh7okF9S003QStjI9Td1d+urqj/k4etwgLiPO8LHaaQ7o2WAwuSXakHJVJ2xIq+T+MYnoobbx5sArk4wsg7AWTX+uw/rXoyN7P9ZAgDSH+4oZ6/0j8dwR6RAoc9c+dog8xnW3eTkeSlj2KYx6XnEOOlOcCBCKsZePi9eoN92CxvB5MRN6tR7w3PH4yynqjZ2VuA6FuxBha4JmmVyysST3avwMxayHfTExp7tnLwHOhdgE="}, 246 }}, 247 // this one has two primary accounts 248 {"zZZijzv+D622csZjyzgZHt/avWYJaHH0S42rO29uBh4=", 249 3, 250 "hKFlxPBByHsjg6VR42RdYX5UsALFZ5XTG348GsP+J1ubWC2Iv+49/NjZtexC9rqQaIVU0yz/oKmJfpBBlB6m3EDjkca/5yszpDPf1WKPQR7tzJMAPpwmbXKC3dWZGO/elRgvGiH3rvq1SVMn5Od20Gkn81rn0w4M2VtiXl23dUqgTPV3zxgnWYgi+qz2MYBQUOCDiIXRQCEoz9uryF36GuI0RhmM5r14zfPTo2Ru6hDqN2FN17aJ/D7xTBiIdQUAlN6cUZS/nQEEEwdxJmlFTzXgoIR7puO0sC9Q1PWMKTfRrCzhUkV/VVyWEMZiQR1VbWii58OjZ2VuA6FuxBh7HP8NWh2qAIdc8bX/gka07BmIGJ6N1BGhdgI=", 251 "g6hhY2NvdW50c5KFqWFjY291bnRJRNk4R0FXWjdIVlBLUkdDSDJLUDY0NzVYVjZIQTJDQUY0NE1YV1dFNVJLVjRMTU1HQjZGTk5TRVBOUEWyYWNjdEJ1bmRsZVJldmlzaW9uAbFlbmNBY2N0QnVuZGxlSGFzaMQgYsYYlXdhMYNv+TjdE9jLU/9InY7g9UFovmMZyVX43SipaXNQcmltYXJ5w6Rtb2RlAYWpYWNjb3VudElE2ThHQlBMTEhPS1BSRlNDRTZGUDZEMzdBVVVOWUNKMlRTUE9ITUhBUjU0VEJETFdXUElaQ0Q0UkwzR7JhY2N0QnVuZGxlUmV2aXNpb24BsWVuY0FjY3RCdW5kbGVIYXNoxCAHEbHL2jsIn5lJJCTkKvM24zKMf+Cu7k52bSSNCaOXsqlpc1ByaW1hcnnDpG1vZGUBpHByZXbAqHJldmlzaW9uAQ==", 252 []accountCan{ 253 {"GAWZ7HVPKRGCH2KP6475XV6HA2CAF44MXWWE5RKV4LMMGB6FNNSEPNPE", "hKFlxKXNijkx4xEPPfrWDSmKzCvDxsaGaqOU+AGRJ21tXT/YiIYVy+Xhqn8ZDA8q7b6NhOLvQQKXao8RaVTyJz2ZPfF4JFdhtB4NW2FvVibNShKFMhpiB2JKKLQ1pe5SWTctKeCMySQEOSuRPYw5h1agWuFSO6G41bjc6tqxSN1Dy3X0NNiTSlV2t+vTKEIxPpslUkraHzX7QzLx0L/UHWgTNSKlDQUFoP2jZ2VuA6FuxBiN3rakOKyhdiI11EnaQh+DJr+OaiDpCwOhdgE="}, 254 {"GBPLLHOKPRFSCE6FP6D37AUUNYCJ2TSPOHMHAR54TBDLWWPIZCD4RL3G", "hKFlxKU/uVJvah1L9M9DXNzqfHkCafEpxeVPaZ+qi7/yxVrYPxaERZe3vtVpSSj/ubXOCps8PQdNGxryD9IpOHc7nz+a+jfGCrl1j5ka6cLaTtVRVPULm4zpFmtj3AG3OMMx3SERt+9nzwCzFMOhsWkF0qsUJmJb147619qHyYXVX9xBhfadKOnam91qDpeezjfkIJNfsc6wNz1Gq/lnw3NFJsWJKhLRPoyjZ2VuA6FuxBjyJpAx80CdDilh3Aa4kpgr2XpPm90HpwShdgE="}, 255 }}, 256 // this one has version 1 257 {"WkgRG8Kn+kJ+9E3UOr+2AL/28+1FAHFcuwaXFNn63Bc=", 258 3, 259 "hKFlxPDOMmhJALH9j+DVPBfgO5o5XoR0e0Wzoohc38H98QuRiDvZdlMSXXVmnXaeESLsdFVvmNBX7LNj8AQ3tsisxuGzUEPnDCIvBQOVqWb0YzCg8hvT5TxuxFaFYr+b7JP+/8JaDfO2ZMZHmwh0bYSy+cveFjmJQu9dqJPrFkaI5M2qE3k9V2d9RDn279l+/tKkXaTADI5si9e8+6ZwccuD+w8YTBhF6pu92Ums9sYwlu1NJhljnzrpnyZHwlkPEuz9bx6gc9flSsTsM+F14z+1/3Mw7dK6/5o3heU6Dp5DRVyYzDm89+Y380nqsdswUItkpDCjZ2VuA6FuxBghv5/O3avrFmYsqX/yOIimwsQV24wATFmhdgE=", 260 "g6hhY2NvdW50c5KFqWFjY291bnRJRNk4R0FXWjdIVlBLUkdDSDJLUDY0NzVYVjZIQTJDQUY0NE1YV1dFNVJLVjRMTU1HQjZGTk5TRVBOUEWyYWNjdEJ1bmRsZVJldmlzaW9uAbFlbmNBY2N0QnVuZGxlSGFzaMQgJ46Gf8Q34Oz+SY5WASuyNRbtK9amEwfZeh+cwYMkhu6paXNQcmltYXJ5w6Rtb2RlAYWpYWNjb3VudElE2ThHQlBMTEhPS1BSRlNDRTZGUDZEMzdBVVVOWUNKMlRTUE9ITUhBUjU0VEJETFdXUElaQ0Q0UkwzR7JhY2N0QnVuZGxlUmV2aXNpb24BsWVuY0FjY3RCdW5kbGVIYXNoxCBT04slGTYXYwS3F/NIMocy4hzfdbd6QbuAcu+fQLdk16lpc1ByaW1hcnnCpG1vZGUBpHByZXbAqHJldmlzaW9uAQ==", 261 []accountCan{ 262 {"GBPLLHOKPRFSCE6FP6D37AUUNYCJ2TSPOHMHAR54TBDLWWPIZCD4RL3G", "hKFlxKVmAT4zpHkRMmxvopFs5dNpPdbLP4Xbuv2vSb3Nb5v+X+5mJCOy+/viCSlFabN0hiLvRA9SNdbhmB0nGGEqr4KTpwI1Igi9kpDHct4WfaO5JKxM9z/c4CKEU+Yp83MwhrfvINFMu/9hvxWfYpIISSQUelfJExn1j+IHaTQje4+bpetdZ8L8aaq0i1JslDBhzuTSut1vDJTOs5IaFUdjmNWlIMFWB8+jZ2VuA6FuxBhp5v8W3hvbFv5pD0YMwazOaadZabL+w9ahdgE="}, 263 {"GAWZ7HVPKRGCH2KP6475XV6HA2CAF44MXWWE5RKV4LMMGB6FNNSEPNPE", "hKFlxKWaCv93H++U6REJYFrDJ7lIkxkIWxQui3TnWgknVao+Ch2mwBXwlrtJIfTLtisMiDe41Rhg5W2/MVuUahM4SP4/7bch/jaco294xDvt9qSy7gV3Tn6y3kQ4D9sbydP5fTX5b/2TbAsYaxVnNBI9gmWz9WnA1i/oMUVb+z64MdWgBFsYb+3NKq+ckOFBx7lWz06W84XVwFOQkfEa9Z1lHO7dZnnbYR2jZ2VuA6FuxBjfxSfGM/YdSvKrrFMopBR2ZqN+/Ekg3WqhdgE="}, 264 }}, 265 // parent visible hash mismatch 266 {"33o4U14XU4NFNg99xV3DrW9+JFqux+Qy+9bKPO0CZqw=", 267 3, 268 "hKFlxPB8pYYB1ikZVIbr42li9xe7uWpnwXj6UzvNl84o1a9BQp32tMp9Os5STGSinCxeKJWThWDEaIkQAhbsIWM5I2f/y5Bakw8qPKdXnPvXzIFdIAKPaL5YOfeH4YqINUy1vLKtmXE4oNN/Re1GI2JJfLHoiwGdfkZ5BwNf8HBMPo+7NQoK72vhmawKoVZ/mhLZWhyWg2o2WLxnI6SvB23zr9S8DuzorjdFyPjMKLoW1cQEvjL89XC3Gh0jVQqtiyY4ztsezSaaK631EgCggKax9TgOMcadtjbSDak4Z4369iPxdPDarifQEH3tCgfQEqfm/FajZ2VuA6FuxBhNSgpvLKdBI1+uyn+AxVitwsUWFOvFv1OhdgI=", 269 "g6hhY2NvdW50c5KFqWFjY291bnRJRNk4R0FXWjdIVlBLUkdDSDJLUDY0NzVYVjZIQTJDQUY0NE1YV1dFNVJLVjRMTU1HQjZGTk5TRVBOUEWyYWNjdEJ1bmRsZVJldmlzaW9uAbFlbmNBY2N0QnVuZGxlSGFzaMQgitIh2ZzE+Ee+B5sOTGh6Zykb3HzJm0AGEStUlbqv3gipaXNQcmltYXJ5w6Rtb2RlAYWpYWNjb3VudElE2ThHQlBMTEhPS1BSRlNDRTZGUDZEMzdBVVVOWUNKMlRTUE9ITUhBUjU0VEJETFdXUElaQ0Q0UkwzR7JhY2N0QnVuZGxlUmV2aXNpb24BsWVuY0FjY3RCdW5kbGVIYXNoxCBha3OfRg0rSijM9oc4jHDzkf6U1T1QA/70ZYyOCclIbqlpc1ByaW1hcnnCpG1vZGUBpHByZXbAqHJldmlzaW9uAQ==", 270 []accountCan{ 271 {"GAWZ7HVPKRGCH2KP6475XV6HA2CAF44MXWWE5RKV4LMMGB6FNNSEPNPE", "hKFlxKVK+UYzZvupFCxmCET7llfg+lz0WgoZjM1A4QwfTrn0SQiyJWUnq25ZHS4yCCkMHt0RZ9nkeNKLdBCW/zbGWl1PazWmRhHrQtnWNxYKn0loHacMxo8XPZrYMJxOzMmAQERYAdKLCCKbX7aSw0fM2f0O0vUakB6G9iMuBkSqLjnItRrw7IDq1nVKfrBKWGA7QndnA+cLU5QRnlc4X3tGnQLqqKiWLDajZ2VuA6FuxBjM4wCrCdDp+PUJMQaXZfid1okMls3wI3ahdgE="}, 272 {"GBPLLHOKPRFSCE6FP6D37AUUNYCJ2TSPOHMHAR54TBDLWWPIZCD4RL3G", "hKFlxKURh2JrnpBAnRNW2lDvx63Sd5bMMqUvI5bkvkn2CNWcMn+FcPRCK+75EZWv0Jnq+KT6Xp0r4Qm8INe27bLQJa5XOB8JB04XY5zGaAwHPY3hddUUmTFfn9CmY46SGY4bSQ5xejPIlsl+VBLgnM+4ZiuRp3o13YNjn9tBdWOHrJmb7c+err863f43Ttw6L1XxbOvwl81VIA/auHkw1znXr8D2ZleOP92jZ2VuA6FuxBgOiuJnWDcJvlZxaI6MyQCMI3leSfE54GWhdgE="}, 273 }}, 274 // account-level hash mismatch 275 {"AdhjUfTyNZvZnyWuLrXCJ2XgpfErFwyqmRkg8ZEjAHg=", 276 3, 277 "hKFlxPAxslJzbyrnYaG4LJUi0hw9jKh6GaM99keZJb7K36pN1whf/bN5iFxrC9J0KQeW7OQAXEp973OdfO2azxzzh3if/SQ9wQN00XQMWLN2Cb0Je98+z9wFWsWdh918Rit5x5hNi61PvTUxQCsahGeO1BqWJxvC3P3XzwBBg2CaesB07KfDZSB5kBP6mruluGgATE4WLmk8LmoyQL4CA6DYpRaWl5Xr6e5tAj5JFzd9wnSCIiKPukONnJqszqfZaF+ZVUznQ1q9MfjIM3huQrOb7wC/NPKtoM+xsTPgCjmIfLZBwY6lD8xiUdaTNGFH6zNvHdCjZ2VuA6FuxBgpHteB4lY/BqjThfRNbn/TNKRfvl0cXv2hdgI=", 278 "g6hhY2NvdW50c5KFqWFjY291bnRJRNk4R0FXWjdIVlBLUkdDSDJLUDY0NzVYVjZIQTJDQUY0NE1YV1dFNVJLVjRMTU1HQjZGTk5TRVBOUEWyYWNjdEJ1bmRsZVJldmlzaW9uAbFlbmNBY2N0QnVuZGxlSGFzaMQgH3M1uaaaaa1r684bDx18YGFfqAPCRhgktz/Y3lBUPAapaXNQcmltYXJ5w6Rtb2RlAYWpYWNjb3VudElE2ThHQlBMTEhPS1BSRlNDRTZGUDZEMzdBVVVOWUNKMlRTUE9ITUhBUjU0VEJETFdXUElaQ0Q0UkwzR7JhY2N0QnVuZGxlUmV2aXNpb24BsWVuY0FjY3RCdW5kbGVIYXNoxCAxAujlppppoT+KYBEoHY76Uq1v9rPcf4lbhFW4v+p14alpc1ByaW1hcnnCpG1vZGUBpHByZXbAqHJldmlzaW9uAQ==", 279 []accountCan{ 280 {"GAWZ7HVPKRGCH2KP6475XV6HA2CAF44MXWWE5RKV4LMMGB6FNNSEPNPE", "hKFlxKWW2fR2MgA3VlejatZlBJ057v0+YFBdSQGuh3V1LkA4XDCYx3fC8+N9FZN7HOlQd1eBYO4gT/7wgg3fk9k2K9BVHCbXAJeKiv8DMV9SbJ7ZWQnXG9BAT1ZQApv4BBVMWTvY9uhao07IU5amC58KC6xlaRZg1BwkRuk4H83ahdvMXWLpzFMlwobtjoD0tiG7u6YGAw3Bpr2N5JUDxdjUAHbdCUSl0BOjZ2VuA6FuxBgk5XMAnRJBS1C7J7g82tAmH85+b2JMWgehdgE="}, 281 {"GBPLLHOKPRFSCE6FP6D37AUUNYCJ2TSPOHMHAR54TBDLWWPIZCD4RL3G", "hKFlxKUSt6xJveOZtTct6TMN9kBo5/1qmoFIHtpCLJzkYrnu0ciOODqDtwnUmHlbYfT7GjRdyN2jo7xfdw6D2jtTKAyCCGUbehlXLFP2x0+wNo6oiUCCiP5/uk3SuR/QfFtrh1aussrZgx2GzW76ZLEXlVKm6tQ+B+/ARQHeQobUaB2jpTrB9HwsO/ZhEllEyWDtRMtaOzlBl3yXPo9e8E41Wq3mFXQRbMWjZ2VuA6FuxBiKSBv9DEiFTGGcAGUcimPbFWeIFm0n99GhdgE="}, 282 }}, 283 } 284 285 func TestCanningFacility(t *testing.T) { 286 if os.Getenv("KEYBASE_CANNING_FACILITY") != "1" { 287 t.Skip("this is not really a test but a tool for creating cans for tests") 288 } 289 a1 := stellar1.AccountID("GAWZ7HVPKRGCH2KP6475XV6HA2CAF44MXWWE5RKV4LMMGB6FNNSEPNPE") 290 s1 := stellar1.SecretKey("SBV2JNAJA65LMCZ5HYDXAYWRQK25CD2DZB25YZVNX3OLPALN2EVKO2V2") 291 a2 := stellar1.AccountID("GBPLLHOKPRFSCE6FP6D37AUUNYCJ2TSPOHMHAR54TBDLWWPIZCD4RL3G") 292 s2 := stellar1.SecretKey("SDZJUFUKEQQU77DCF2VH72XB4S427EGKF6BSOSPUIKLTBCTCMXQQ7JU5") 293 bundleLocal := stellar1.Bundle{ 294 Revision: 1, 295 Prev: nil, 296 Accounts: []stellar1.BundleEntry{{ 297 AccountID: a1, 298 Mode: stellar1.AccountMode_USER, 299 IsPrimary: true, 300 Name: "p1", 301 AcctBundleRevision: 1, 302 EncAcctBundleHash: nil, 303 }, { 304 AccountID: a2, 305 Mode: stellar1.AccountMode_USER, 306 IsPrimary: false, 307 Name: "p2", 308 AcctBundleRevision: 1, 309 EncAcctBundleHash: nil, 310 }}, 311 AccountBundles: map[stellar1.AccountID]stellar1.AccountBundle{ 312 a1: { 313 AccountID: a1, 314 Signers: []stellar1.SecretKey{s1}, 315 }, 316 a2: { 317 AccountID: a2, 318 Signers: []stellar1.SecretKey{s2}, 319 }, 320 }, 321 } 322 ring := newPukRing() 323 pukSeed, pukGen := ring.makeGen(t, 3) 324 boxed, err := BoxAndEncode(&bundleLocal, pukGen, pukSeed) 325 require.NoError(t, err) 326 t.Logf(spew.Sdump(boxed)) 327 t.Logf("puk seed: %v", toB64(pukSeed[:])) 328 t.Logf("puk gen: %v", pukGen) 329 t.Logf("nonce: %v", toB64(boxed.EncParent.N[:])) 330 t.Logf("enc E: %v", toB64(boxed.EncParent.E)) 331 t.Logf("\nEncParentB64: %v", boxed.EncParentB64) 332 t.Logf("VisParentB64: %v\n", boxed.VisParentB64) 333 for acctID, encodedAcct := range boxed.AcctBundles { 334 t.Logf("account: %v, EncB64: %v", acctID, encodedAcct.EncB64) 335 } 336 cipherpack, err := base64.StdEncoding.DecodeString(boxed.EncParentB64) 337 require.NoError(t, err) 338 encHash := sha256.Sum256(cipherpack) 339 t.Logf("actual own hash: %v", toB64(encHash[:])) 340 341 // decode it back again and take a look, 342 // especially for generating expected errors 343 benc := BundleEncoded{ 344 EncParent: boxed.EncParentB64, 345 VisParent: boxed.VisParentB64, 346 AcctBundles: make(map[stellar1.AccountID]string), 347 } 348 for acctID, encodedAcct := range boxed.AcctBundles { 349 benc.AcctBundles[acctID] = encodedAcct.EncB64 350 } 351 m := libkb.NewMetaContext(context.Background(), nil) 352 decodedBundle, _, _, _, err := DecodeAndUnbox(m, ring, benc) 353 t.Logf("decoded: %+v, err: %v", decodedBundle, err) 354 } 355 356 func toB64(b []byte) string { 357 return base64.StdEncoding.EncodeToString(b) 358 } 359 360 func TestCanned(t *testing.T) { 361 m := libkb.NewMetaContext(context.Background(), nil) 362 363 // valid can 364 c := cans[0] 365 bundle, _, _, _, err := DecodeAndUnbox(m, c.ring(t), c.toBundleEncodedB64()) 366 require.NoError(t, err) 367 require.Equal(t, "yJwcMuMxwpFuxt0A+7zYT2iev/1wVB5OeNdJzDSlBDo=", toB64(bundle.OwnHash)) 368 // hashes match for the first account 369 a1BundleHash := bundle.AccountBundles["GAWZ7HVPKRGCH2KP6475XV6HA2CAF44MXWWE5RKV4LMMGB6FNNSEPNPE"].OwnHash 370 require.Equal(t, "BgkhqzuIynMJrhIeuSOPTCoS5QutvwDXZr7fHVuCmpg=", toB64(a1BundleHash)) 371 require.Equal(t, "BgkhqzuIynMJrhIeuSOPTCoS5QutvwDXZr7fHVuCmpg=", toB64(bundle.Accounts[0].EncAcctBundleHash)) 372 // hashes match for the second account 373 a2BundleHash := bundle.AccountBundles["GBPLLHOKPRFSCE6FP6D37AUUNYCJ2TSPOHMHAR54TBDLWWPIZCD4RL3G"].OwnHash 374 require.Equal(t, "mwbjopTOaCgbwtSWhMyaKvJdVtaYnFeTMZExDqzxJ6U=", toB64(a2BundleHash)) 375 require.Equal(t, "mwbjopTOaCgbwtSWhMyaKvJdVtaYnFeTMZExDqzxJ6U=", toB64(bundle.Accounts[1].EncAcctBundleHash)) 376 } 377 378 func TestCantOpenWithTheWrongKey(t *testing.T) { 379 m := libkb.NewMetaContext(context.Background(), nil) 380 381 c := cans[1] 382 pukSeed := c.puk(t) 383 pukGen := c.gen() 384 ring := &pukRing{ 385 map[keybase1.PerUserKeyGeneration]libkb.PerUserKeySeed{ 386 pukGen: pukSeed, 387 }, 388 } 389 _, _, _, _, err := DecodeAndUnbox(m, ring, c.toBundleEncodedB64()) 390 require.Error(t, err) 391 require.Contains(t, err.Error(), "secret box open failed") 392 } 393 394 func TestCannedUnboxInvariantViolationMultiplePrimary(t *testing.T) { 395 m := libkb.NewMetaContext(context.Background(), nil) 396 397 c := cans[2] 398 _, _, _, _, err := DecodeAndUnbox(m, c.ring(t), c.toBundleEncodedB64()) 399 require.Error(t, err) 400 require.Contains(t, err.Error(), "multiple primary accounts") 401 } 402 403 func TestCannedCryptV1(t *testing.T) { 404 m := libkb.NewMetaContext(context.Background(), nil) 405 406 c := cans[3] 407 _, _, _, _, err := DecodeAndUnbox(m, c.ring(t), c.toBundleEncodedB64()) 408 require.Error(t, err) 409 require.Contains(t, err.Error(), "stellar secret bundle encryption version 1 has been retired") 410 } 411 412 func TestCannedBundleHashMismatch(t *testing.T) { 413 m := libkb.NewMetaContext(context.Background(), nil) 414 415 c := cans[4] 416 _, _, _, _, err := DecodeAndUnbox(m, c.ring(t), c.toBundleEncodedB64()) 417 require.Error(t, err) 418 require.Contains(t, err.Error(), "corrupted bundle: visible hash mismatch") 419 } 420 421 func TestCannedAccountHashMismatch(t *testing.T) { 422 m := libkb.NewMetaContext(context.Background(), nil) 423 424 c := cans[5] 425 _, _, _, _, err := DecodeAndUnbox(m, c.ring(t), c.toBundleEncodedB64()) 426 require.Error(t, err) 427 require.Contains(t, err.Error(), "account bundle and parent entry hash mismatch") 428 } 429 430 // TestBoxAccountBundle checks boxing an account bundle and that DecodeAndUnbox 431 // gets back to the initial bundle. 432 func TestBoxAccountBundle(t *testing.T) { 433 b, err := NewInitial("abc") 434 require.NoError(t, err) 435 require.NotNil(t, b) 436 437 ring := newPukRing() 438 seed, gen := ring.makeGen(t, 1) 439 boxed, err := BoxAndEncode(b, gen, seed) 440 require.NoError(t, err) 441 require.NotNil(t, boxed, "BoxAndEncode() should return something") 442 require.Equal(t, stellar1.BundleVersion_V2, boxed.FormatVersionParent, "should be V2") 443 require.NotEmpty(t, boxed.VisParentB64) 444 require.NotEmpty(t, boxed.EncParentB64) 445 require.Equal(t, 2, boxed.EncParent.V) 446 require.NotEmpty(t, boxed.EncParent.E) 447 require.NotZero(t, boxed.EncParent.N) 448 require.Equal(t, gen, boxed.EncParent.Gen) 449 require.Len(t, boxed.AcctBundles, 1) 450 451 m := libkb.NewMetaContext(context.Background(), nil) 452 bundle, version, pukGen, accountGens, err := DecodeAndUnbox(m, ring, boxed.toBundleEncodedB64()) 453 require.NoError(t, err) 454 require.NotNil(t, bundle) 455 require.Equal(t, stellar1.BundleVersion_V2, version) 456 require.Len(t, bundle.Accounts, 1) 457 require.Equal(t, stellar1.AccountMode_USER, bundle.Accounts[0].Mode) 458 require.Equal(t, pukGen, keybase1.PerUserKeyGeneration(1)) 459 acctBundle, ok := bundle.AccountBundles[bundle.Accounts[0].AccountID] 460 require.True(t, ok) 461 acctBundleOriginal, ok := b.AccountBundles[bundle.Accounts[0].AccountID] 462 require.True(t, ok) 463 require.Equal(t, acctBundle.Signers[0], acctBundleOriginal.Signers[0]) 464 for _, acct := range bundle.Accounts { 465 require.Equal(t, keybase1.PerUserKeyGeneration(1), accountGens[acct.AccountID]) 466 } 467 } 468 469 // pukRing is a convenience type for puks in these tests. 470 type pukRing struct { 471 puks map[keybase1.PerUserKeyGeneration]libkb.PerUserKeySeed 472 } 473 474 func newPukRing() *pukRing { 475 return &pukRing{puks: make(map[keybase1.PerUserKeyGeneration]libkb.PerUserKeySeed)} 476 } 477 478 func (p *pukRing) makeGen(t *testing.T, gen int) (libkb.PerUserKeySeed, keybase1.PerUserKeyGeneration) { 479 puk, err := libkb.GeneratePerUserKeySeed() 480 require.NoError(t, err) 481 pgen := keybase1.PerUserKeyGeneration(gen) 482 p.puks[pgen] = puk 483 return puk, pgen 484 } 485 486 // SeedByGeneration makes pukRing implement PukFinder. 487 func (p *pukRing) SeedByGeneration(m libkb.MetaContext, generation keybase1.PerUserKeyGeneration) (libkb.PerUserKeySeed, error) { 488 puk, ok := p.puks[generation] 489 if ok { 490 return puk, nil 491 } 492 return libkb.PerUserKeySeed{}, errors.New("not found") 493 }