summaryrefslogtreecommitdiff
path: root/src/Crypto/Tox.hs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Crypto/Tox.hs')
-rw-r--r--src/Crypto/Tox.hs65
1 files changed, 50 insertions, 15 deletions
diff --git a/src/Crypto/Tox.hs b/src/Crypto/Tox.hs
index 9f86f6a4..307a5db5 100644
--- a/src/Crypto/Tox.hs
+++ b/src/Crypto/Tox.hs
@@ -428,27 +428,62 @@ getPublicKey = throwCryptoError . publicKey <$> S.getBytes 32
428putPublicKey :: PublicKey -> S.Put 428putPublicKey :: PublicKey -> S.Put
429putPublicKey bs = S.putByteString $ BA.convert bs 429putPublicKey bs = S.putByteString $ BA.convert bs
430 430
431-- 32 bytes -> 42 base64 digits.
432--
431encodeSecret :: SecretKey -> Maybe C8.ByteString 433encodeSecret :: SecretKey -> Maybe C8.ByteString
432encodeSecret k = do 434encodeSecret k = do
433 (a,bs) <- BA.uncons (BA.convert k) 435 (a,bs) <- BA.uncons (BA.convert k)
436 -- Bytes
437 -- 1 31
438 -- a | bs
434 (cs,c) <- unsnoc bs 439 (cs,c) <- unsnoc bs
435 let a' = shiftR a 1 .|. (shiftR c 4 .&. 0x03) 440 -- Bytes
441 -- 1 30 1
442 -- a | cs | c
443 --
444 -- Based on the following pasted from the generateSecretKey function:
445 --
446 -- tweakToSecretKey :: ScrubbedBytes -> SecretKey
447 -- tweakToSecretKey bin = SecretKey $ B.copyAndFreeze bin $ \inp -> do
448 -- modifyByte inp 0 (\e0 -> e0 .&. 0xf8)
449 -- modifyByte inp 31 (\e31 -> (e31 .&. 0x7f) .|. 0x40)
450 --
451 -- We know the following holds:
452 -- a == a .&. 0xf8
453 -- c == (c .&. 0x7f) .|. 0x40
454 --
455 -- Therefore, there are 5 reserved bits:
456 -- a := aaaa a000
457 -- c := 01dd cccc
458 --
459 -- That gives us 256 - 5 = 251 bits to encode.
460 -- 42 * 6 = 252
461 --
462 let -- We'll reserve the first bit as zero so that the encoded
463 -- key starts with a digit between A and f. Other digits will be
464 -- arbitrary.
465 --
466 -- The middle 30 bytes will be encoded as is from the source byte
467 -- string (cs). It remains to compute the first (a') and last (c')
468 -- bytes.
469 xs = Base64.encode $ a' `BA.cons` cs `BA.snoc` c'
470 -- a' := 0aaaaadd
471 a' = shiftR a 1 .|. (shiftR c 4 .&. 0x03)
472 -- c' := cccc0000
436 c' = shiftL c 4 473 c' = shiftL c 4
437 xs = Base64.encode $ cs `BA.snoc` a' `BA.snoc` c' 474 return $ BA.take 42 xs
438 (ys,ds) = BA.splitAt 40 xs
439 return $ BA.index ds 0 `BA.cons` ys `BA.snoc` BA.index ds 1
440 475
476-- 42 base64 digits. First digit should be between A and f. The rest are
477-- arbitrary.
441decodeSecret :: C8.ByteString -> Maybe SecretKey 478decodeSecret :: C8.ByteString -> Maybe SecretKey
479decodeSecret k64 | B.length k64 < 42 = Nothing
442decodeSecret k64 = do 480decodeSecret k64 = do
443 (ds0,ysds1) <- BA.uncons k64 481 xs <- either (const Nothing) Just $ Base64.decode $ B.append k64 "A="
444 (ys,ds1) <- unsnoc ysds1 482 (a',ds) <- B.uncons $ B.take 32 xs
445 let k64' = B.append ys (BA.cons ds0 (BA.cons ds1 "A=")) 483 (cs,c') <- B.unsnoc ds
446 k <- either (const Nothing) Just $ Base64.decode k64' 484 let c = 0x40 .|. shiftR c' 4 .|. ( 0x30 .&. shiftL a' 4)
447 (csa,c') <- unsnoc k 485 a = 0xf8 .&. shiftL a' 1
448 (cs,a') <- unsnoc csa 486 case secretKey $ B.cons a cs `B.snoc` c of
449 let a = shiftL (a' .&. 0x7c) 1
450 c = shiftR c' 4 .|. (shiftL a' 4 .&. 0x30) .|. 0x40
451 let r = a `BA.cons` (cs `BA.snoc` c)
452 case secretKey r of
453 CryptoPassed x -> Just x 487 CryptoPassed x -> Just x
454 _ -> Nothing 488 _ -> Nothing
489