From VisualWorks®, 7.6 of February 1, 2008 on February 1, 2008 at 12:31:43 pm (Dialog confirm: 'You are filing-in a Parcel source file!\\While this is possible it will not have\the same effect as loading the parcel.\None of the Parcel''s prerequisites will\be loaded and none of its load actions\will be performed.\\Are you sure you want to file-in?' withCRs) ifFalse: [self error: 'Parcel file-in abandoned. Choose terminate or close.'] <class> <name>DHInvalidGroupParameters</name> <environment>Security</environment> <super>Core.Error</super> <private>false</private> none <imports></imports> <category>DH</category> <attributes> <package>DH</package> </attributes> </class> <comment> Security.DHInvalidGroupParameters
RFC2631 requires that the group parameters are large primes such that q divides p - 1, i.e. there is such j > 1, that p=q*j + 1. The primary purpose of the validation is to prevent a so called 'small subgroup attack' on the sender's key pair. If this is not the case this exception is signaled.
</comment> <class> <name>DHInvalidPublicKey</name> <environment>Security</environment> <super>Core.Error</super> <private>false</private> none <imports></imports> <category>DH</category> <attributes> <package>DH</package> </attributes> </class> <comment> Security.DHInvalidPublicKey
RFC2631 suggests that received public key y, is validated using following criteria: 1) 1 < y < p-1 2) y^g mod p = 1 This error is signaled when the public key doesn't meet this criteria.
</comment> <class> <name>DHError</name> <environment>Security</environment> <super>Core.Error</super> <private>false</private> none <imports></imports> <category>DH</category> <attributes> <package>DH</package> </attributes> </class> <comment> Security.DHError
Generic superclass for DH errors
</comment> <class> <name>DH</name> <environment>Security</environment> <super>Security.AsymmetricCipher</super> <private>false</private> none p q g random x y random <imports></imports> <category>DH</category> <attributes> <package>DH</package> </attributes> </class> <comment> Security.DH
This is an implementation of the Diffie-Hellman algorithm as described in RFC2631 (http://www.ietf.org/rfc/rfc2631.txt). Specifically, generation of the shared secret is implemented, but not the rest of the specification dealing with the key derivation process. For consistency, the terminology (parameter names) in RFC2631 is reused here. This implementation can be used to establish a shared secret between two parties over an insecure communication channel. Note, however, that this algorithm only protects against eaves-dropping attacks; it cannot protect against an active man-in-the-middle attack where the adversary is able to modify messages in transit. An additional authentication mechanism must be employed to prevent such attacks. Obtaining a shared secrent using the DH computation consists of several steps. First, both parties agree on a set of algorithm parameters (p, q and g) each will use for the computations. Next, these parameters are used by each party to generate a private value x and a public value y. The parties exchange their public y values, and use them to separately compute the shared secret. The shared secret is not easily broken because neither the private x values nor the shared secret itself are exposed in open communication. Finally, the shared secret is used by each party to generate the same secret key. Again, both parties should have a previous agreement on what method will be used to generate the secret key from the shared secret. Usually, the secret has to be at least adjusted to the expected key form or size for the target algorithm. The initial algorithm parameters are p, q and g as follows: p modulus, a large prime (>=512 bits) q order, another large prime (>= 160 bits) such that p=q*j + 1, where j > 1 g generator, such that g > 1 and g=h^j mod p, where 1 < h < p-1, g has order q mod p The parameters are generated using a parameter generator. See the class comment of DHParameterGenerator for more details about this process. Parameters can be precomputed (often contained in the subject key of a certificate), in which case an instance of the algorithm can be created as follows: DH p: p q: q g: g Or they can be generated on the fly as an automatic part of computing a public value, like so: DH new publicValue If one party generates the parameters, they have to be communicated to the other party along with the public value. The parameters do not need to be kept secret, so the same insecure channel can be used for the parameters as well. For example, if alice wants to share a secret with bob, alice may generate the parameters automatically as shown above: alice := DH new. alicePublic := alice publicValue. Now alice communicates these parameters to bob in some way, as appropriate for the particular application: dhParameters := Protocols.Struct new p: alice p; q: alice q; g: alice g; yourself. <alice send: dhParameters to: bob> and bob uses the parameters generated by alice to compute his own public value: bob := DH p: dhParameters p q: dhParameters q g: dhParameters g. bobPublic := bob publicValue. Now each party sends its public value to the other party, again using some appropriate form of communication. Each party can then compute a shared secret using the other party's public value: aliceSecret := alice sharedSecretUsing: bobPublic. bobSecret := bob sharedSecretUsing: alicePublic. Neither the private value x nor the shared secret is communicated, which makes the secret difficult to discover, even if p, q, g, and the two public values y are known. But the computational process guaranties that both alice and bob obtain the same secret value: aliceSecret = bobSecret The size (number of bits) of the parameters (especially p) affect the level of security. Here are some important points to remember: - the size of p must be at least 512 bits - the size of p should at least 1024 bits to be considered strong - the size of q must be at least 160 bits - the size of q should be at least twice the size of the key that will be generated from the shared secret So for 64-bit keys, the minimum of 160-bits for q is plenty. However, 128-bit keys should use a q of at least 256 bits. Note that the public value y and the shared secret are the same size as p, and the size of the private key x is up to the size of q. So the key lengths can be tuned by providing apropriately sized p and q. If the DH algorithm is instantiated without the q parameter, as is done in SSL key exchanges, it can still perform the shared secret computation. However, it will generate the private value x at roughly the same size as p. That makes the computation much slower. If we take the case of 160-bit long q and 1024-bit p, the algorithm will perform about 6-times faster than using 1024-bit p without q. Ensuring the ability to use even bigger keys is also recommended, even if it would reduce the performance of current systems below desired levels. Since future research breakthroughs could weaken the strength of the algorithm, switching to longer keys at that point can buy some time to adapt to the resulting security threats. Instance Variables: p <LargePositiveInteger> large prime (512bits or more) q <LargePositiveInteger> large prime (160-bits or more), such that p=q*j + 1, where j >= 2 g <Integer> the generator, such that g > 1 and g=h^((p-1)/q)) mod p, where 1 < h < p-1, random <Random> generator for private values x x <LargePositiveInteger> randomly generated private key, should be 1 < x < q - 1 y <LargePositiveInteger> public key, such that y = g^x mod p. Class Instance Variables: random <Random> the default random generator
</comment> <class> <name>DHParameterGenerator</name> <environment>Security</environment> <super>Core.EventManager</super> <private>false</private> none m l random primalityTest sha q p g seed counter random <imports></imports> <category>DH</category> <attributes> <package>DH</package> </attributes> </class> <comment> Security.DHParameterGenerator
This is the generator for Diffie-Hellman algorithm parameters p, q and g as described in RFC2631 (http://www.ietf.org/rfc/rfc2631.txt). Parameter q has to be generated first, then parameter p and finally parameter g. An instance of the generator has to be instructed what is the expected bit size of the parameters. The bit-size of q is referred to as m and the bit-size of p is referred to as l. The bit-size of g can be anywhere up to the size of p. Following expression creates a generator for q of size 256 bits and p of size 1024 bits: generator := DHParameterGenerator m: 256 l: 1024. Parameters are generated automatically when accessed. p := generator p. q := generator q. g := generator g. The parameters are cached, so the accessors will keep returning the same values until the generator is flushed. Flushing resets the generated values, which will be regenerated the next time they are accessed. The generator also answers #seed and #counter which can be used by other parties to verify the quality (cryptographic strength) of the parameters. The generator makes use of an algorithm for testing primality of large numbers and a random number generator. In the default configuration it uses the algorithms described by the RFC, however it is possible to configure the generator with other algorithms if necessary. See methods #m:l:random: and #m:l:random:primalityTest: for details. Since generation can take quite some time, events are triggered to indicate progress. See the class method #eventsTriggered for details. See also the class comment for DH for more details about the algorithm, especially on recommended sizes of parameters. Instance Variables: m <SmallInteger> the bit-length of q (minimum 160) l <SmallInteger> the bit-length of p (minimum 512) random <Random> a random seed generator producing large intergers of at least m-bit size primalityTest <PrimalityTest> a probabilistic primality test, with error probability less than 1/(4^50) sha <SHA> an instance of the hash function q <LargeInteger> order, a large prime of bit-length m p <LargeInteger> modulus, a large prime of bit-length l, such that p=jq+1, where j > 1 g <Integer> generator, such that g > 1 and g=h^j mod p, where 1 < h < p-1; i.e. g has order q mod p seed <LargeInteger> the seed used to generate q counter <SmallInteger> the counter value that yielded p Class Instance Variables: random <Random> the default random generator
</comment> <methods> Security.DHInvalidGroupParameters class <category>testing</category>
mayResume ^true
</methods> <methods> Security.DHInvalidPublicKey class <category>testing</category>
mayResume ^true
</methods> <methods> Security.DH <category>accessing</category>
g ^g
p ^p
privateKey ^x ifNil: [ self generateX ]
privateKey: aPrivateKey self shouldNotImplement
publicKey ^y ifNil: [ self generateY ]
publicKey: aPublicKey self shouldNotImplement
q ^q
random ^random ifNil: [random := self defaultRandom]
x ^x
y ^y
</methods> <methods> Security.DH <category>initialization</category>
p: modulus g: generator p := modulus. g := generator
p: modulus q: order g: generator | j | j := (modulus - 1)/order. (j isInteger and: [j > 1]) ifFalse: [ DHInvalidGroupParameters raiseWith: (Array with: p with: q) errorString: (#BadGroupParameters << #security >> 'The group parameters do not meet RFC2631 criteria!') ]. q := order. self p: modulus g: generator
random: aRandom random := aRandom
x: aPrivateNumber y: aPublicNumber "This is for using predefined keys, e.g. from DH certificates and such. Note that x and y are required to satisfy y=g^x mod p" x := aPrivateNumber. y := aPublicNumber
</methods> <methods> Security.DH <category>deprecated</category>
computeG | j h | j := (p - 1)/q. h := 1. [ h := h + 1. (g := h raisedTo: j modulo: p) = 1 ] whileTrue. ^g
computePublicKey "This is the first phase of the handshake, where the publicly exchanged value is generated" ^self computePublicValue
computePublicValue "This is the first phase of the handshake, where the publicly exchanged value is generated" ^self publicValue
computeSharedSecretUsing: aPublicKey "This is the second phase of the handshake where the shared secret is generated using the public value received from the other party" ^self sharedSecretUsing: aPublicKey
p: modulus q: order "This method was based on the assumption that the generator parameter g is produced deteministically and therefore can be reliably derived from the p and q parameters. However, properly it should be a random choice over a specified interval and thust the assumption is not valid. That's why this API is being deprecated. " self p: modulus q: order g: nil. g := self computeG
</methods> <methods> Security.DH <category>services</category>
decrypt: aByteArray "Decrypt aByteArray using the previously assigned private key." "This algorithm doesn't support encryption" ^self shouldNotImplement
encrypt: aByteArray "Encrypts aByteArray using the previously assigned public key" "This algorithm doesn't support encryption" ^self shouldNotImplement
flush "Flush the keys so that the next call to computePublicKey generates new ones" x := y := nil
publicValue "This is the first phase of the handshake, where the publicly exchanged value is generated" ^self publicKey
sharedSecretUsing: aPublicValue "This is the second phase of the handshake where the shared secret is generated using the public value received from the other party" (1 < aPublicValue and: [ aPublicValue < p ]) ifFalse: [ DHInvalidPublicKey new messageText: (#PublicValueOutOfRange << #security >> 'Public value out of required range <1; p - 1>!'); parameter: aPublicValue; raiseSignal ]. "This is required check as per RFC2631, section 2.1.5" (q isNil or: [ (aPublicValue raisedTo: q modulo: p) = 1 ]) ifFalse: [ DHInvalidPublicKey new messageText: (#PublicValueVulnerable << #security >> 'Public value possibly vulnerable'); parameter: aPublicValue; raiseSignal ]. ^aPublicValue raisedTo: x modulo: p
sign: aMessageArray "Compute a signature of aMessageArray." "This algorithm doesn't support signing" ^self shouldNotImplement
verify: aSignatureArray of: aMessageArray "Denies or confirms that aSignatureArray is a signature of aMessageArray." "This algorithm doesn't support signing" ^self shouldNotImplement
</methods> <methods> Security.DH <category>private</category>
defaultRandom ^self class random
generateParameters | generator | generator := DHParameterGenerator m: 160 l: 512. self p: generator p q: generator q g: generator g
generateX | mod | p ifNil: [ self generateParameters ]. mod := q ifNil: [ p ]. x := self nextRandomSize: mod basicSize. x := x \\ mod. " the x from [ 2, q - 2 ] constraint is from section 2.2. Key and Parameter Requirements" ^( x between: 2 and: mod - 2 ) ifTrue: [ x ] ifFalse: [ self generateX ]
generateY x ifNil: [ self generateX ]. ^y := g raisedTo: x modulo: p
nextRandomSize: size " size <Integer> size in bytes " x := self random next. [ x basicSize < size ] whileTrue: [ | chunk | chunk := self random next. x := (x bitShift: (chunk basicSize * 8)) bitOr: chunk ]. ^x basicSize > size ifTrue: [ x bitShift: (size - x basicSize) * 8 ] ifFalse: [ x ]
</methods> <methods> Security.DH class <category>accessing</category>
defaultRandom ^DSSRandom default
random ^random ifNil: [ self defaultRandom ]
random: aRandom "Set the default random generator for DH algorithms. aRandom <Random> secure random generator " random := aRandom
</methods> <methods> Security.DH class <category>instance creation</category>
newFrom: generator "This is useful when the parameters are generated on the fly, it makes sense to reuse the random generator that was used to generate the parameters" ^(self p: generator p q: generator q g: generator g ) random: generator random; yourself
p: modulus g: generator " modulus <LargePositiveInteger> must be a large prime (512 bits and more). generator <Integer> must be an integer, such that g > 1 and g=h^((p-1)/q)) mod p, where 1 < h < p-1 (g has order q mod p, i.e. g^q mod p = 1) " ^self new p: modulus g: generator; yourself
p: modulus q: order g: generator " modulus <LargePositiveInteger> must be a large prime (512 bits and more). order <LargePositiveInteger> must be a large prime (160 bits and more) that divides (modulus - 1) generator <Integer> must be an integer, such that g > 1 and g=h^((p-1)/q)) mod p, where 1 < h < p-1 (g has order q mod p, i.e. g^q mod p = 1) " ^self new p: modulus q: order g: generator; yourself
</methods> <methods> Security.DH class <category>deprecated</category>
p: modulus q: order "This method was based on the assumption that the generator parameter g is produced deteministically and therefore can be reliably derived from the p and q parameters. However, properly it should be a random choice over a specified interval and thust the assumption is not valid. That's why this API is being deprecated. modulus must be a large prime (512 bits and more). order must be a large prime (160 bits and more) that divides (modulus - 1) " ^self new p: modulus q: order; yourself
</methods> <methods> Security.DHParameterGenerator <category>accessing</category>
counter ^counter
g g ifNil: [ self generateG ]. ^g
l ^l
l: anObject l := anObject
m ^m
m: anObject m := anObject
p p ifNil: [ self generateP ]. ^p
primalityTest ^primalityTest
primalityTest: anObject primalityTest := anObject
q q ifNil: [ self generateQ ]. ^q
random ^random
random: anObject random := anObject
seed ^seed
seed: anObject seed := anObject
sha ^sha
sha: anObject sha := anObject
</methods> <methods> Security.DHParameterGenerator <category>initialization</category>
m: qLength l: pLength " qLength <SmallInteger> the bit-length of q, at least 160 pLength <SmallInteger> the bit-length of p, at least 512" self m: qLength l: pLength random: self defaultRandom
m: qLength l: pLength random: aRandom " qLength <SmallInteger> the bit-length of q, at least 160 pLength <SmallInteger> the bit-length of p, at least 512 aRandom <Random> a random seed generator producing large intergers of size >= m bits" self m: qLength l: pLength random: aRandom primalityTest: (self defaultPrimalityTestUsing: aRandom)
m: qLength l: pLength random: aRandom primalityTest: pTest " qLength <SmallInteger> the bit-length of q, at least 160 pLength <SmallInteger> the bit-length of p, at least 512 aRandom <Random> a random seed generator producing large intergers of size >= m bits pTest <PrimalityTest> a probabilistic primality test, with error probability less than 1/4^50" m := qLength. l := pLength. random := aRandom. primalityTest := pTest. sha := SHA new.
</methods> <methods> Security.DHParameterGenerator <category>services</category>
flush "Flush all the parameters to make sure next generation creates new ones" q := p := seed := counter := nil
generateG | j h | p ifNil: [self generateP]. j := (p - 1)/q. [ h := self nextH. (g := h raisedTo: j modulo: p) = 1 ] whileTrue. ^g
generateP "Generates parameter P and a counter value. RFC2631, Section 2.2.1.1" | mp2 lp n power power2 r v w x q2 | q ifNil: [ self generateQ ]. mp2 := (m / 160) ceiling * 2. lp := (l / 160) ceiling. n := (l / 1024) ceiling * 4096. power := 2 raisedTo: l - 1. power2 := power * 2. counter := 0. q2 := q * 2. [ n > counter ] whileTrue: [ r := seed + mp2 + (lp * counter). v := 0. 0 to: lp - 1 do: [ :i | v := ((sha hashInteger: r + i) bitShift: 160 * i) + v ]. w := v \\ power2. x := w bitOr: power. p := x - (x \\ q2) + 1. self triggerEvent: #tryingP:counter: with: p with: counter. (p > power and: [ primalityTest value: p ]) ifTrue: [ self triggerEvent: #foundP:counter: with: p with: counter. ^self ]. counter := counter + 1 ]. ^DHError raiseSignal: (#ParameterGenerationFailed << #security >> 'Failed to generate parameters !')
generateQ "Generates a seed and parameter Q. RFC2631, Section 2.2.1.1" | mp u power qAndMask qOrMask | p := counter := nil. mp := (m / 160) ceiling. power := 2 raisedTo: m - 1. qAndMask := power * 2 - 1. qOrMask := power bitOr: 1. [ seed := self newSeed. u := 0. 0 to: mp - 1 do: [ :i | u := ( ( (sha hashInteger: seed + i) bitXor: (sha hashInteger: seed + mp + i) ) bitShift: 160 * i ) bitOr: u ]. q := (u bitAnd: qAndMask) bitOr: qOrMask. self triggerEvent: #tryingQ:seed: with: q with: seed. primalityTest value: q ] whileFalse. self triggerEvent: #foundQ:seed: with: q with: seed
</methods> <methods> Security.DHParameterGenerator <category>private</category>
defaultPrimalityTestUsing: aRandom ^PrimeSieve on: (MillerRabin random: aRandom)
defaultRandom ^self class random
newSeed "RFC2631, Section 2.2.1.1, Step 4. Select an arbitrary bit string SEED such that the length of SEED >= m " | newSeed | newSeed := random next. "Need at least m random bits" [ newSeed highBit < m ] whileTrue: [ | chunk | chunk := random next. newSeed := (newSeed bitShift: chunk highBit) bitOr: chunk ]. ^newSeed
nextH "RFC2631, Section 2.2.1.2, step 2. Set h = any integer, where 1 < h < p - 1 and h differs from any value previously tried. Note that we're relying on the quality of the random generator here. Given that secure generators have to have an extremely long period the chances that a value would be repeated are fairly slim. " | h | h := random next. "Need l random bits" [ h basicSize * 8 < l ] whileTrue: [ | chunk | chunk := random next. h := (h bitShift: chunk basicSize * 8) bitOr: chunk ]. h := h basicSize * 8 > l ifTrue: [ h bitShift: l - (h basicSize * 8) ] ifFalse: [ h ]. ^(h between: 2 and: p - 2) ifFalse: [ self nextH ] ifTrue: [ h ]
</methods> <methods> Security.DHParameterGenerator class <category>events</category>
eventsTriggered ^#( tryingQ:seed: foundQ:seed: tryingP:counter: foundP:counter:)
</methods> <methods> Security.DHParameterGenerator class <category>instance creation</category>
m: qLength l: pLength " qLength <SmallInteger> the bit-length of q, at least 160 pLength <SmallInteger> the bit-length of p, at least 512" ^self new m: qLength l: pLength
m: qLength l: pLength random: aRandom " qLength <SmallInteger> the bit-length of q, at least 160 pLength <SmallInteger> the bit-length of p, at least 512 aRandom <Random> a random seed generator producing large intergers of size >= m bits" ^self new m: qLength l: pLength random: aRandom
</methods> <methods> Security.DHParameterGenerator class <category>accessing</category>
defaultRandom ^DSSRandom default
random ^random ifNil: [ self defaultRandom ]
random: aRandom "Set the default random generator for DH parameter generators. aRandom <Random> secure random generator " random := aRandom
</methods> "Imported Classes:" self error: 'Attempting to file-in parcel imports. Choose terminate or close' <class> <name>EventManager</name> <environment>Core</environment> <super>Core.Object</super> <private>false</private> none handlers <imports></imports> <category>System-Dependency Events</category> <attributes> <package>System-Dependency Events</package> </attributes> </class> <class> <name>Error</name> <environment>Core</environment> <super>Core.Exception</super> <private>false</private> none <imports></imports> <category>Kernel-Exception Handling</category> <attributes> <package>Kernel-Exception Handling</package> </attributes> </class> <class> <name>AsymmetricCipher</name> <environment>Security</environment> <super>Core.Object</super> <private>false</private> none <imports></imports> <category>Security-Ciphers</category> <attributes> <package>CiphersBase</package> </attributes> </class>