-
Notifications
You must be signed in to change notification settings - Fork 2
/
cryptopan.go
172 lines (151 loc) · 6.13 KB
/
cryptopan.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/*
* Copyright (c) 2014, Yawning Angel <yawning at schwanenlied dot me>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
// Package cryptopan implements the Crypto-PAn prefix-preserving IP address
// sanitization algorithm as specified by J. Fan, J. Xu, M. Ammar, and S. Moon.
//
// Crypto-PAn has the following properties:
//
// * One-to-one - The mapping from original IP addresses to anonymized IP
// addresses is one-to-one.
//
// * Prefix-preserving - In Crypto-PAn, the IP address anonymization is
// prefix-preserving. That is, if two original IP addresses share a k-bit
// prefix, their anonymized mappings will also share a k-bit prefix.
//
// * Consistent across traces - Crypto-PAn allows multiple traces to be
// sanitized in a consistent way, over time and across locations. That is,
// the same IP address in different traces is anonymized to the same
// address, even though the traces might be sanitized separately at
// different time and/or at different locations.
//
// * Cryptography-based - To sanitize traces, trace owners provide Crypto-PAn
// a secret key. Anonymization consistency across multiple traces is
// achieved by the use of the same key. The construction of Crypto-PAn
// preserves the secrecy of the key and the (pseudo)randomness of the
// mapping from an original IP address to its anonymized counterpart.
//
// As an experimental extension, anonymizing IPv6 addresses is also somewhat
// supported, but is untested beyond a cursory examination of the output.
package cryptopan
import (
"crypto/aes"
"crypto/cipher"
"crypto/subtle"
"net"
"strconv"
)
const (
// Size is the length of the Crypto-PAn keying material.
Size = keySize + blockSize
blockSize = aes.BlockSize
keySize = 128 / 8
)
// KeySizeError is the error returned when the provided key is an invalid
// length.
type KeySizeError int
func (e KeySizeError) Error() string {
return "invalid key size " + strconv.Itoa(int(e))
}
type bitvector [blockSize]byte
func (v *bitvector) SetBit(idx, bit uint) {
byteIdx := idx / 8
bitIdx := 7 - idx&7
oldBit := uint8((v[byteIdx] & (1 << bitIdx)) >> bitIdx)
flip := 1 ^ subtle.ConstantTimeByteEq(oldBit, uint8(bit))
v[byteIdx] ^= byte(flip << bitIdx)
}
func (v *bitvector) Bit(idx uint) uint {
byteIdx := idx / 8
bitIdx := 7 - idx&7
return uint((v[byteIdx] & (1 << bitIdx)) >> bitIdx)
}
// Cryptopan is an instance of the Crypto-PAn algorithm, initialized with a
// given key.
type Cryptopan struct {
aesImpl cipher.Block
pad bitvector
}
// Anonymize anonymizes the provided IP address with the Crypto-PAn algorithm.
func (ctx *Cryptopan) Anonymize(addr net.IP) net.IP {
var obfsAddr []byte
if v4addr := addr.To4(); v4addr != nil {
obfsAddr = ctx.anonymize(v4addr)
return net.IPv4(obfsAddr[0], obfsAddr[1], obfsAddr[2], obfsAddr[3])
} else if v6addr := addr.To16(); v6addr != nil {
// None of the other implementations in the wild do something like
// this, but there's no reason I can think of beyond "it'll be really
// slow" as to why it's not valid.
obfsAddr = ctx.anonymize(v6addr)
addr := make(net.IP, net.IPv6len)
copy(addr[:], obfsAddr[:])
return addr
}
panic("unsupported address type")
}
func (ctx *Cryptopan) anonymize(addr net.IP) []byte {
addrBits := uint(len(addr) * 8)
var origAddr, input, output, toXor bitvector
copy(origAddr[:], addr[:])
copy(input[:], ctx.pad[:])
// The first bit does not take any bits from orig_addr.
ctx.aesImpl.Encrypt(output[:], input[:])
toXor.SetBit(0, output.Bit(0))
// The rest of the one time pad is build by copying orig_addr into the AES
// input bit by bit (MSB first) and encrypting with ECB-AES128.
for pos := uint(1); pos < addrBits; pos++ {
// Copy an additional bit into input from orig_addr.
input.SetBit(pos-1, origAddr.Bit(pos-1))
// ECB-AES128 the input, only one bit of output is used per iteration.
ctx.aesImpl.Encrypt(output[:], input[:])
// Note: Per David Stott@Lucent, using the MSB of the PRF output leads
// to weaker anonymized output. Jinliang Fan (one of the original
// Crypto-PAn authors) claims that a new version that incorporates one
// of his suggested tweaks is forthcoming, but it looks like that never
// happened, and no one else does that.
//
// Something like: toXor.SetBit(pos, output.Bit(pos)) will fix this,
// but will lead to different output than every other implementation.
toXor.SetBit(pos, output.Bit(0))
}
// Xor the pseudorandom one-time-pad with the address and return.
for i := 0; i < len(addr); i++ {
toXor[i] ^= origAddr[i]
}
return toXor[:len(addr)]
}
// New constructs and initializes Crypto-PAn with a given key.
func New(key []byte) (ctx *Cryptopan, err error) {
if len(key) != Size {
return nil, KeySizeError(len(key))
}
ctx = new(Cryptopan)
if ctx.aesImpl, err = aes.NewCipher(key[0:keySize]); err != nil {
return nil, err
}
ctx.aesImpl.Encrypt(ctx.pad[:], key[keySize:])
return
}