notes

Personal notes
git clone git://git.laack.co/notes.git
Log | Files | Refs

LamportSignature.md (4811B)


      1 # Lamport Signature
      2 
      3 **Source:** MIT MAS.S62 Cryptocurrency Engineering and Design L1
      4 
      5 **Definition:** The Lamport signature scheme is a single use signature scheme where portions of a private key are leaked to prove the private key is known by the sender of the message, as a way to sign the message.
      6 
      7 ## Specifics
      8 
      9 ### Key Generation
     10 
     11 To generate a private key you generate a pair of 256 256 bit segments of random data. Since 256 bits is 32 bytes, we see the total size of the private key is 16kb (16384 bytes)
     12 
     13 To generate the public key we perform hashing on the data from the private key. Specifically, since the sha256 hash function gives hashes that are exactly 256 bits, we apply the hash function to each of the 512 segments of 256 bits above to generate a public key where each of the resulting segments is the hash of the associated segment from the private key. It is still worthwhile to consider the public key as two keys each with 256 segments, but for the purposes of public key generation this doesn't matter. 
     14 
     15 ### Signing Messages
     16 
     17 To sign a message we first hash the message using the sha256 hash function. This gives us 256 bits of data to work with. 
     18 
     19 Using these 256 bits of data, we perform the operations specified in the below pseudocode:
     20 
     21 ```
     22 
     23 signature = ""
     24 for idx in range(hash)
     25     if hash[idx] == 0:
     26         signature += privateKey.first[idx]
     27     if hash[idx] == 1:
     28         signature += privateKey.second[idx]
     29 return signature
     30 
     31 ```
     32 
     33 The signature is thus exactly half of the private key where the half revealed is (generally) some portion of the first half and some portion second half of the private key, based on value of the current bit from the message hash.
     34 
     35 ### Verifying Messages
     36 
     37 To verify a message we iterate over each of the bits from the message's hash and verify the segments revealed as the private key hash to the associated segments of the public key. 
     38 
     39 ```
     40 for idx in range(hash):
     41     if hash[idx] == 0:
     42         assert sha256sum(privateKey.first[idx]) == publicKey[idx]
     43     if hash[idx] == 1:
     44         assert sha256sum(privateKey.second[idx]) == publicKey[idx]
     45 ```
     46 
     47 ## Limitations
     48 
     49 - The keys for lamport signatures, along with the signatures themselves, are large
     50     - public / private keys are 16kb
     51     - signatures are 8kb
     52 - Keys should only be used once
     53     - One iteration leaks half of the key
     54     - Two iterations leaks ~3/4 of the key
     55         - Depending on the message being signed, even more segments may be revealed
     56     - ...
     57 
     58 ## Benefits
     59 
     60 - Simple to implement
     61 - Cheap to compute
     62     - No large primes
     63 - Quantum resistant
     64     - Impacted by grover's algorithm so expect 128 bits of security instead of 256 when facing quantum computers
     65         - This is generally considered acceptable
     66 
     67 ## Implementation
     68 
     69 ```go
     70 
     71 package main
     72 
     73 import (
     74 	"crypto/rand"
     75 	"crypto/sha256"
     76 	"fmt"
     77 )
     78 
     79 type Key struct {
     80 	firstRow  [256][32]byte
     81 	secondRow [256][32]byte
     82 }
     83 
     84 func GenerateKeys() (Key, Key) {
     85 
     86 	privateKey := Key{}
     87 
     88 	for i := range 256 {
     89 		_, err := rand.Read(privateKey.firstRow[i][:])
     90 		if err != nil {
     91 			panic(err)
     92 		}
     93 	}
     94 
     95 	for i := range 256 {
     96 		_, err := rand.Read(privateKey.secondRow[i][:])
     97 		if err != nil {
     98 			panic(err)
     99 		}
    100 	}
    101 
    102 	publicKey := Key{}
    103 
    104 	for i := range 256 {
    105 		publicKey.firstRow[i] = sha256.Sum256(privateKey.firstRow[i][:])
    106 	}
    107 
    108 	for i := range 256 {
    109 		publicKey.secondRow[i] = sha256.Sum256(privateKey.secondRow[i][:])
    110 
    111 	}
    112 
    113 	return publicKey, privateKey
    114 }
    115 
    116 func Sign(privateKey Key, message string) [256][32]byte {
    117 
    118 	messageHash := sha256.Sum256([]byte(message))
    119 
    120 	signature := [256][32]byte{}
    121 
    122 	for index := range 32 {
    123 		currentByteString := fmt.Sprintf("%08b", messageHash[index])
    124 		for idx := range 8 {
    125 			currentBit := currentByteString[idx]
    126 			if currentBit == '0' {
    127 				signature[(index*8)+idx] = privateKey.firstRow[(index*8)+idx]
    128 			} else {
    129 				signature[(index*8)+idx] = privateKey.secondRow[(index*8)+idx]
    130 			}
    131 		}
    132 	}
    133 
    134 	return signature
    135 }
    136 
    137 func Verify(publicKey Key, message string, signature [256][32]byte) bool {
    138 
    139 	messageHash := sha256.Sum256([]byte(message))
    140 
    141 	for index := range 32 {
    142 		currentByteString := fmt.Sprintf("%08b", messageHash[index])
    143 		for idx := range 8 {
    144 			currentBit := currentByteString[idx]
    145 			if currentBit == '0' {
    146 				// signature[(index * 8) + idx] = privateKey.firstRow[(index * 8) + idx]
    147 				if sha256.Sum256(signature[(index*8)+idx][:]) != publicKey.firstRow[(index*8)+idx] {
    148 					return false
    149 				}
    150 			} else {
    151 				if sha256.Sum256(signature[(index*8)+idx][:]) != publicKey.secondRow[(index*8)+idx] {
    152 					return false
    153 				}
    154 			}
    155 		}
    156 	}
    157 
    158 	return true
    159 
    160 }
    161 
    162 func main() {
    163 
    164 	publicKey, privateKey := GenerateKeys()
    165 	message := rand.Text()
    166 	signature := Sign(privateKey, message)
    167 	valid := Verify(publicKey, message, signature)
    168 	fmt.Printf("Verified message matches signature: %v\n", valid)
    169 
    170 }
    171 ```